معرفی سریع

به طور کلی تا قبل از نسخه 8 جاوا،‌ هربار قصد داشتیم تا عملکردی را پیاده‌سازی کنیم،‌ مجبور بودیم‌ آن را داخل یک کلاس قرار دهیم. به عبارت دیگر هر تابعی که می‌نویسیم،‌ بخشی از یک کلاس و درواقع بخشی از یک شیء خواهد بود.

این مسئله از آنجا که زبان جاوا (و زبان اندروید) شیء گراست،‌ اصلا نمی‌تواند اتفاق عجیبی باشد. اما گاهی این مسئله برای ما محدودیت ایجاد می‌کند. مثلا اگر بخواهیم تابعی برای جمع دو عدد بنویسیم،‌ چه نیازی است که حتما آن را در قالب یک کلاس بنویسیم؟  آیا بهتر نبود که بتوانیم صرفا “عملکرد” هایمان را بنویسیم و نیازی به تعریفِ  کلاس‌ها برای آنها نداشتیم؟

بهتر نمی‌شد اگر می‌توانستیم یک قدم جلو‌تر رفته و حتی با علمکرد‌ها مثل متغیر‌ها رفتار کنیم؟

یعنی همانطور که اعداد صحیح،‌ اعداد اعشاری و رشته‌ها و امثال آن را تعریف و از آنها استفاده می‌کنیم،‌ بتوانیم عملکرد‌ها را هم به شکل متغیر تعریف کنیم و از آن در بخش‌های مختلف برنامه استفاده کنیم.

 

این یکی از مزیت‌ها و اهداف عبارات لامبداست.

علاوه بر این،‌ عبارات لامبدا به ما اجازه پردازش موازی کار‌ها را نیز می‌دهند.

 

عبارات لامبدا از Functional interface‌ها برای تعریف خود استفاده می‌کنند (اگر نمی‌دانید،‌ بدانید که اینترفیس‌هایی که صرفا یک تابع abstract داخل خود دارند را functional interface می‌نامند‌؛‌ مثلا اگر به اینترفیس Runnable که در اندروید کاربرد زیادی هم دارد نگاهی بی‌اندازیم،‌ می‌بینیم که فقط یک تابع run داخل خود دارد و به تبع،‌ functional interface است).

 

عملگر فلش

عبارات لامبدا یک عملگر جدید که عملگر فلش یا Arrow‌ نام دارد را به جاوا اضافه کرده است. این عملگر عبارات لامبدا را به دو بخش تقسیم می‌کند:

(n) -> n*n

سمت چپ پارامتر‌هایی را که برای عبارت مورد نیاز است را مشخص می‌کند،‌  که اگر پارامتری مورد نیاز نیست،‌ می‌تواند خالی باشد.

سمت راست بدنه عبارت لامبدا است که عملیات عبارت لامبدا را مشخص می‌کند.

بد نیست اگر به این عملگر به عنوان واژه‌ی “می‌شود”‌ نگاه کنیم. مثلا “n می‌شود n*n”.

با داشتن دانش در مورد عملگر فلش و functional interfaceها،‌ می‌شود یک عبارت ساده لامبدا را پیاده‌سازی کرد:

interface NumericTest {
	boolean computeTest(int n); 
}

public static void main(String args[]) {
	NumericTest isEven = (n) -> (n % 2) == 0;
	NumericTest isNegative = (n) -> (n < 0);

	// Output: false
	System.out.println(isEven.computeTest(5));

	// Output: true
	System.out.println(isNegative.computeTest(-5));
}

وقتی یک عبارت لامبدا می‌نویسید،‌ می‌توانید نوع پارامتر را نیز در عبارت مثل این تعریف کنید:

MyGreeting morningGreeting = (String str) -> "Good Morning " + str + "!";
MyGreeting eveningGreeting = (String str) -> "Good Evening " + str + "!";

قراردادن عبارات لامبدا در بلاک

تا الان،‌ عبارات لامبدای تک‌خطی را دیدیم، یک نوع دیگر هم از عبارات لامبدا،‌ وقتی است که سمتِ‌ راستِ‌ فلش چند خط داشته باشیم،‌ در این حالت لامبدا را در بلاک نوشته و در انتهای بلاک سیمی‌کالن قرار می‌دهیم. مثلا:

interface MyString {
	String myStringFunction(String str);
}

public static void main (String args[]) {
	// Block lambda to reverse string
	MyString reverseStr = (str) -> {
		String result = "";
		
		for(int i = str.length()-1; i >= 0; i--)
			result += str.charAt(i);
		
		return result;
	};

	// Output: omeD adbmaL
	System.out.println(reverseStr.myStringFunction("Lambda Demo")); 
}

 

توابع Functional‌ عمومی

یک عبارت لامبدا نمی‌تواند به صورت generic نوشته شود. اما functional interface نسبت داده شده به عبارت لامبدا می‌تواند. می‌شود یک اینترفیس generic نوشت و از آن برای پشتیبانی از انواع خروجی و ورودی‌های مختلف استفاده کرد:

interface MyGeneric<T> {
	T compute(T t);
}

public static void main(String args[]){

	// String version of MyGenericInteface
	MyGeneric<String> reverse = (str) -> {
		String result = "";
		
		for(int i = str.length()-1; i >= 0; i--)
			result += str.charAt(i);
		
		return result;
	};

	// Integer version of MyGeneric
	MyGeneric<Integer> factorial = (Integer n) -> {
		int result = 1;
		
		for(int i=1; i <= n; i++)
			result = i * result;
		
		return result;
	};

	// Output: omeD adbmaL
	System.out.println(reverse.compute("Lambda Demo")); 

	// Output: 120
	System.out.println(factorial.compute(5));

عبارات لامبدا به عنوان ورودی توابع

یکی از کاربرد‌های متداول عبارات لامبدا،‌ این است که از آنها به عنوان ورودی توابع استفاده کنیم.

این قابلیت خیلی کاربردی‌ است،‌ چون اجازه اینکه کد‌های اجرایی را به عنوان آرگومان به توابع پاس کنیم را به ما می‌دهد.

 

برای ارسال یک عبارت لامبدا به عنوان ورودی تابع،‌ فقط باید مطمئن باشید که نوع functional interface با نوع مورد انتظار همخوانی دارد.

interface MyString {
	String myStringFunction(String str);
}

public static String reverseStr(MyString reverse, String str){
  return reverse.myStringFunction(str);
}

public static void main (String args[]) {
	// Block lambda to reverse string
	MyString reverse = (str) -> {
		String result = "";
		
		for(int i = str.length()-1; i >= 0; i--)
			result += str.charAt(i);
		
		return result;
	};

	// Output: omeD adbmaL
	System.out.println(reverseStr(reverse, "Lambda Demo"));

آموزش ویدئویی:

قسمت اول:

قسمت دوم: