4. تابعهای لامبدا در جاوا ۸
4.1. مثال-۰۱ – رابطهای تابعی و لامبداها
![]() |
کد زیر را در نظر بگیرید:
package dvp.java8.lambdas;
public class Exemple01 {
public static void main(String[] args) {
//کلاسهای ناشناس
I1 ia1 = new I1() {
@Override
public void doSomething() {
System.out.println("ia1.doSomething");
}
};
I2 ia2 = new I2() {
@Override
public String getSomething(double value) {
return String.format("ia2.getSomething(%s)", value);
}
};
//لامبداها
I1 ib1 = () -> System.out.println("ib1.lambda");
I2 ib2 = (value) -> String.format("ib2.lambda(%s)", value);
I1 ib3 = () -> {
System.out.println("ib3.lambda");
};
I2 ib4 = (double value) -> {
return String.format("ib4.lambda(%s)", value);
};
// app
ia1.doSomething();
System.out.println(ia2.getSomething(4.3));
ib1.doSomething();
System.out.println(ib2.getSomething(5.8));
ib3.doSomething();
System.out.println(ib4.getSomething(10.1));
}
}
@FunctionalInterface
interface I1 {
void doSomething();
}
@FunctionalInterface
interface I2 {
String getSomething(double value);
}
- خطوط ۴۱–۴۴: یک رابط تابعی I1 تعریف میشود. یک رابط تابعی، رابطی است که تنها یک متد دارد. این وابسته به وجود anotation [@FunctionalInterface] در خط ۴۱ نیست، که اختیاری است؛
- خطوط ۴۶–۴۹: یک رابط تابعی دوم، I2؛
- خطوط ۶–۱۹: رابطهای I1 و I2 با استفاده از کلاسهای ناشناس پیادهسازی شدهاند، که رایجترین رویکرد پیش از معرفی توابع لامبدا بود؛
- خطوط ۲۱–۲۹: رابطهای I1 و I2 با استفاده از توابع لامبدا پیادهسازی شدهاند؛
- خطوط ۷–۱۲: پیادهسازی رابط I1 با استفاده از کلاس ناشناس. نحو پیادهسازی یک رابط I با استفاده از کلاس ناشناس به صورت زیر است:
جایی که m1، m2، ... متدهایی هستند که توسط رابط I تعریف شدهاند.
- خطوط 14–19: پیادهسازی رابط I2 با استفاده از یک کلاس ناشناس؛
- خط ۲۲: پیادهسازی رابط I1 با استفاده از تابع لامبدا. در اینجا از این واقعیت بهره میبریم که رابط تابعی تنها یک متد دارد. بنابراین تابع لامبدا این متد واحد M را پیادهسازی میکند. نحو آن به شرح زیر است:
انواع T1، T2 و Tn را میتوان حذف کرد اگر کامپایلر بتواند آنها را از زمینه استنتاج کند (استنتاج نوع).
- خط ۲۲: پیادهسازی متد [I1.doSomething] با امضای زیر:
void doSomething();
[doSomething] روشی است که هیچ پارامتری نمیگیرد و هیچ نتیجهای بازنمیگرداند. پیادهسازی لامبدا آن میتواند همانطور که در خط ۲۲ نشان داده شده یا مانند خطوط ۲۴–۲۶ نوشته شود، یعنی میتوان کروشههای منحنی را دور کد تابع لامبدا قرار داد. اگر این کد فقط شامل یک عبارت باشد، مانند اینجا، میتوان این کروشههای منحنی را حذف کرد؛
- خط ۲۳: پیادهسازی متد [I1.getSomething] با امضای زیر:
String getSomething(double value);
[getSomething] پارامتری از نوع [double] را میپذیرد و نتیجهای از نوع [String] را برمیگرداند. پیادهسازی لامبدا آن میتواند آن در خط ۲۳ یا آن در خطوط ۲۷–۲۹ باشد. در پیادهسازی روی خط ۲۳:
- نوع پارامتر [value] حذف شده است. بنابراین، از نوع [double] که در امضای [getSomething] یافت میشود، استفاده خواهد شد؛
- کد لامبدا در کروشهها قرار نگرفته است. بنابراین نتیجه لامبدا، مقدار عبارت واحد درون آن کد است، که در این مورد عبارت است از: String.format("ib2.lambda(%s)", value);
در پیادهسازی در خطوط 27–29:
- نوع پارامتر [value] صراحتاً اعلام شده است؛
- یک [return] برای نمایش نتیجهٔ لامبدا استفاده میشود. در این مورد، باید از پرانتزهای منحنی استفاده کنید؛
- خطوط ۳۲–۳۷: توابع ناشناس و لامبداهای مختلف فراخوانی میشوند؛
نتیجهٔ بهدستآمده به شرح زیر است:
4.2. مثال-۰۲ – رابط تابعی Predicate<T>
![]() |
در بیشتر موارد، ما با رابطهای تابعی از کتابخانهها سر و کار داریم تا رابطهای تابعی که خودمان تعریف میکنیم. در اینجا، ما در حال بررسی رابط تابعی [Predicate] هستیم که در بسته [java.util.function] تعریف شده است، بستهای که شامل اکثر رابطهای تابعی در جاوا ۸ است. این رابط به صورت زیر تعریف شده است:

ما اشاره کردیم که یک رابط تابعی تنها یک متد دارد. با این حال، در اینجا چهار متد وجود دارد. نوآوری دیگر معرفیشده توسط جاوا ۸، مفهوم متد پیشفرض در یک رابط بود که با کلمه کلیدی [default] مشخص میشود. در اینجا سه متد از این نوع داریم. این متدها از این جهت متمایز هستند که پیادهسازی پیشفرض دارند. بنابراین، کلاسی که یک رابط را با متدهای پیشفرض پیادهسازی میکند، ملزم به پیادهسازی آن متدها نیست. بدین ترتیب، کلاسی که میخواهد رابط [Predicate] را پیادهسازی کند، تنها یک متد دارد که باید آن را پیادهسازی کند: متد [test]. بنابراین، رابط [Predicate] در واقع یک رابط تابعی است. از این رو میتوان گفت که یک رابط تابعی، رابطی است که پیادهسازی آن تنها شامل یک متد اجباری است. اگر بیش از یک متد داشته باشد، متدهای دیگر باید کلمه کلیدی [default] را داشته باشند.
متد [Predicate<T>.test] یک پارامتر از نوع T را میپذیرد و یک مقدار بولی (Boolean) را برمیگرداند. این رابط عموماً برای فیلتر کردن مجموعهها (collections) استفاده میشود. برای نشان دادن نحوه استفاده از آن، از دادههای زیر استفاده خواهیم کرد:
package dvp.data;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Personne {
public enum Sexe {HOMME,FEMME};
// دادهها
private String nom;
private int age;
private double poids;
private Sexe sexe;
// سازندهها
public Personne() {
}
public Personne(String nom, Sexe sexe, int age, double poids) {
this.nom = nom;
this.sexe=sexe;
this.age = age;
this.poids = poids;
}
// گیرنده و تنظیمکننده
...
// toString
@Override
public String toString(){
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
return e.getMessage();
}
}
}
- خطوط ۳۲–۳۸: متد [toString] رشته مربوط به شخص را برمیگرداند jSON;
کلاس [Personnes] فهرستی از ۳ نفر را تعریف میکند:
package dvp.data;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.List;
public class Personnes {
private static List<Personne> personnes = Arrays.asList(new Personne("jean", Personne.Sexe.HOMME, 20, 70),
new Personne("marie", Personne.Sexe.FEMME, 10, 30), new Personne("camille", Personne.Sexe.FEMME, 30, 55));
public static List<Personne> get() {
return personnes;
}
public static String toString(List<Personne> liste) {
try {
return new ObjectMapper().writeValueAsString(liste);
} catch (JsonProcessingException e) {
return e.getMessage();
}
}
}
- خطوط ۱۰–۱۱: لیست ۳ نفره؛
- خطوط ۱۳–۱۵: یک متد استاتیک برای بازیابی این لیست؛
- خطوط 17–23: یک متد استاتیک برای بازیابی رشته jSON از فهرستی از افراد که بهعنوان پارامتر ارسال شده است؛
این دادهها توسط کد زیر استفاده خواهند شد:
package dvp.java8.lambdas;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple02 {
public static void main(String[] args) {
// پ्रेडیکات پیادهسازیشده توسط یک کلاس ناشناس
Predicate<Personne> filterPoids=new Predicate<Personne>() {
@Override
public boolean test(Personne personne) {
return personne.getPoids()<50;
}
};
// پ्रेडیکات پیادهسازیشده توسط یک لامبدا
Predicate<Personne> filterAge = p -> p.getAge() < 28;
// فهرست افراد
List<Personne> personnes = Personnes.get();
// نماها
System.out.println(Personnes.toString(filterPersonnes(personnes, filterAge)));
System.out.println(Personnes.toString(filterPersonnes(personnes, filterPoids)));
}
private static List<Personne> filterPersonnes(List<Personne> personnes, Predicate<Personne> filter) {
// [filter] فهرست [personnes] را فیلتر میکند
List<Personne> personnesFiltrées = new ArrayList<>();
for (Personne p : personnes) {
if (filter.test(p)) {
personnesFiltrées.add(p);
}
}
return personnesFiltrées; }
}
- خطوط ۱۳–۱۸: پیادهسازی رابط Predicate<Person> با استفاده از یک کلاس ناشناس. این یک فیلتر بر اساس وزن شخص است؛
- خط ۲۰: پیادهسازی رابط Predicate<Person> با استفاده از تابع لامبدا. این یک فیلتر بر اساس سن شخص است. همانطور که قبلاً ذکر شد، میتوانست به شکل زیر نیز نوشته شود:
Predicate<Personne> filterAge = (Personne p) -> {
return (p.getAge() < 28);
};
اما نسخه خط ۲۰ مختصرتر است. نوع پارامتر p از زمینه استنباط میشود. در اینجا، یک نوع [Predicate<Personne>] ساخته میشود. بنابراین، متد پیادهسازیشده دارای امضای [boolean test(Personne param)] است. در نتیجه، نوع ضمنی p در خط ۲۰، نوع [Personne] است؛
- خط ۲۲: یک فهرست از پیش تعریفشده از افراد بازیابی میشود؛
- خط 24: آنها بر اساس سن فیلتر میشوند؛
- خط ۲۵: ما آنها را بر اساس وزن فیلتر میکنیم. در هر دو مورد، ما رشته jSON را از لیست فیلتر شده نمایش میدهیم؛
- خطوط 28–37: یک متد استاتیک که
- بهعنوان پارامترها: یک لیست از افراد برای فیلتر شدن و فیلتر را میگیرد. فیلتر نمونهای از رابط [Predicate<Personne>] است. برای سهولت، در اینجا و در سایر بخشهای سند، «نمونهای از یک رابط I» به نمونهای از کلاسی C که I را پیادهسازی میکند، اشاره دارد؛
- فهرست فیلترشده را بازمیگرداند؛
- خط ۳۲: از متد [test] از رابط [Predicate] استفاده میشود. بسته به فیلتری که به متد ارسال میشود، متد [test] خواهد بود:
return personne.getPoids()<50;
یا
return p.getAge() < 28
اجرای کلاس [Exemple02] نتیجه زیر را تولید میکند:
4.3. مثال-۰۳ – رابط تابعی Function<T,R>
![]() |
رابط تابعی Function<T,R> به صورت زیر تعریف میشود:

تنها متد این رابط امضای R apply(T t) را دارد. این متد معمولاً برای ایجاد یک نوع جدید Collection<R> از یک نوع Collection<T> استفاده میشود. برای نمایش این رابط، از کد زیر استفاده خواهیم کرد:
package dvp.java8.lambdas;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dvp.data.Personne;
import dvp.data.Personnes;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class Exemple03 {
public static void main(String[] args) throws JsonProcessingException {
//با استفاده از یک کلاس ناشناس
Function<Personne, String> mapToName = new Function<Personne, String>() {
@Override
public String apply(Personne personne) {
return personne.getNom();
}
};
//پیادهسازی با استفاده از لامبدا
Function<Personne, Integer> mapToAge = p -> p.getAge();
// فهرست افراد
List<Personne> personnes = Personnes.get();
//jSON
ObjectMapper jsonMapper = new ObjectMapper();
//نمایشها
System.out.println(jsonMapper.writeValueAsString(mapPersonnes(personnes, mapToName)));
System.out.println(jsonMapper.writeValueAsString(mapPersonnes(personnes, mapToAge)));
}
// تبدیل از List<Person> به List<T>
private static <T> List<T> mapPersonnes(List<Personne> personnes, Function<Personne, T> mapper) {
List<T> maps = new ArrayList<>();
for (Personne p : personnes) {
maps.add(mapper.apply(p));
}
return maps;
}
}
- خطوط ۱۵–۲۰: ما رابط [Function<Personne, String>] را با یک کلاس ناشناس پیادهسازی میکنیم که تبدیل از Person به String را انجام میدهد؛
- خط ۲۲: ما رابط [Function<Personne, Integer>] را با استفاده از یک عبارت لامبدا پیادهسازی میکنیم که تبدیل از Person به Integer را انجام میدهد؛
- خطوط ۳۳–۳۹: یک متد استاتیک که
- دو پارامتر میگیرد: اولی، از نوع List<Personne>، فهرستی از افراد برای تبدیل شدن است. دومی، از نوع Function<Person, T>، تابعی است که برای هر فرد در فهرست، یک شیء از نوع T ایجاد میکند؛
- نوع List<T> را بازمیگرداند که در آن هر عنصر نتیجه تبدیل Person → T است؛
- خطوط ۳۵–۳۷: تبدیل Person → T اعمال میشود. اگر پارامتر دوم متد، شیء [mapToName] باشد، تبدیل Person → String انجام خواهد شد. اگر این شیء [mapToAge] باشد، تبدیل Person -> Integer انجام خواهد شد؛
نتیجهٔ بهدستآمده به شرح زیر است:
4.4. مثال-۰۴ – رابط تابعی Consumer<T>
![]() |
رابط تابعی Consumer<T> به شرح زیر تعریف میشود:

تنها متد این رابط امضای زیر را دارد: void accept(T t). این متد پارامتر خود را مصرف میکند و هیچ نتیجهای بازنمیگرداند. برای نمایش این موضوع، از کد زیر استفاده خواهیم کرد:
package dvp.java8.lambdas;
import java.util.List;
import java.util.function.Consumer;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple04 {
public static void main(String[] args) {
// فهرست افراد
List<Personne> personnes = Personnes.get();
// پیادهسازی ناشناس
Consumer<Personne> consumerAge = new Consumer<Personne>() {
@Override
public void accept(Personne personne) {
System.out.printf(" age de %s = %s%n", personne.getNom(), personne.getAge());
}
};
// پیادهسازی لامبدا
Consumer<Personne> consumerPoids = p -> System.out.printf(" poids de %s = %s%n", p.getNom(), p.getPoids());
//نمایشها
for (Personne p : personnes) {
consumerAge.accept(p);
}
System.out.println("--------");
for (Personne p : personnes) {
consumerPoids.accept(p);
}
}
}
- خطوط ۱۴–۱۹: رابط [Consumer<Personne>] توسط یک کلاس ناشناس پیادهسازی شده است که متد [accept] آن نام و سن شخص را نمایش میدهد؛
- خط ۲۱: رابط [Consumer<Personne>] توسط یک تابع لامبدا پیادهسازی شده است که متد ضمنی آن [accept] نام و وزن شخص را نمایش میدهد؛
- خطوط ۲۳–۲۵: لیست افراد توسط پیادهسازی [consumerAge] مصرف میشود؛
- خطوط ۲۷–۲۹: لیست افراد توسط پیادهسازی [consumerPoids] مصرف میشود؛
نتایج به شرح زیر است:
4.5. مثال-05 – رابط کاربری BiConsumer<T,U>
![]() |
رابط تابعی BiConsumer<T,U> به شرح زیر تعریف شده است:

تنها متد این رابط امضای زیر را دارد: void accept(T t, U u). این متد یک نوع T را به همراه اطلاعات اضافی U u میپذیرد. ما کاربرد آن را با کد زیر نشان خواهیم داد:
package dvp.java8.lambdas;
import dvp.data.Personne;
import dvp.data.Personnes;
import java.util.List;
import java.util.function.BiConsumer;
public class Exemple05 {
public static void main(String[] args) {
// فهرست افراد
List<Personne> personnes = Personnes.get();
// پیادهسازی ناشناس
BiConsumer<Personne, Integer> biconsumerAge = new BiConsumer<Personne, Integer>() {
@Override
public void accept(Personne personne, Integer integer) {
personne.setAge(personne.getAge() + integer);
System.out.printf("age de %s = %s%n", personne.getNom(), personne.getAge());
}
};
// پیادهسازی لامبدا
BiConsumer<Personne, Integer> biconsumerPoids = (p, i) -> {
p.setPoids(p.getPoids() + i);
System.out.printf("poids de %s = %s%n", p.getNom(), p.getPoids());
};
// دیدها
for (Personne p : personnes) {
biconsumerAge.accept(p, 100);
}
System.out.println("--------");
for (Personne p : personnes) {
biconsumerPoids.accept(p, 200);
}
}
}
- خطوط 14–20: پیادهسازی رابط BiConsumer<T,U> با استفاده از یک کلاس ناشناس. متد [apply] از پارامتر دوم خود برای بهروزرسانی سن شخص ارسالشده بهعنوان پارامتر اول استفاده میکند. سپس نتیجه را نمایش میدهد؛
- خطوط 22–25: پیادهسازی رابط BiConsumer<T,U> با استفاده از یک تابع لامبدا. متد ضمنی [apply] از پارامتر دوم خود برای بهروزرسانی وزن فردی که بهعنوان پارامتر اول ارسال شده است، استفاده میکند. سپس نتیجه را نمایش میدهد؛
- خطوط ۲۷–۲۹: لیست افراد با استفاده از پیادهسازی [biconsumerAge] پردازش میشود؛
- خطوط ۳۱–۳۳: لیست افراد با استفاده از پیادهسازی [biconsumerPoids] پردازش میشود؛
نتایج بهدستآمده به شرح زیر است:
4.6. مثال-۰۶ – رابط عملکردی BiFunction<T,U,R>
![]() |
رابط عملکردی BiFunction<T,U,R> به شرح زیر تعریف شده است:

تنها متد این رابط امضای زیر را دارد: R apply(T t, U u). این متد مشابه متد [BiConsumer.apply] است، اما در حالی که دومی نتیجهای بر نمیگرداند، متد [BiFunction.apply] نتیجه را برمیگرداند. ما کاربرد آن را با کد زیر نشان خواهیم داد:
package dvp.java8.lambdas;
import java.util.List;
import java.util.function.BiFunction;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple06 {
public static void main(String[] args) {
// فهرست افراد
List<Personne> personnes = Personnes.get();
// پیادهسازی ناشناس
BiFunction<Personne, Integer, Integer> biFunctionAge = new BiFunction<Personne, Integer, Integer>() {
@Override
public Integer apply(Personne personne, Integer integer) {
return personne.getAge() + integer;
}
};
// پیادهسازی لامبدا
BiFunction<Personne, Integer, Double> biFunctionPoids = (p, i) -> {
return p.getPoids() + i;
};
//نمایشها
for (Personne p : personnes) {
System.out.printf("age de %s = %s%n", p.getNom(), biFunctionAge.apply(p, 100));
}
System.out.println("--------");
for (Personne p : personnes) {
System.out.printf("poids de %s = %s%n", p.getNom(), biFunctionPoids.apply(p, 200));
}
}
}
- خطوط 14–19: رابط BiFunction<Person, Integer, Integer> با استفاده از یک کلاس ناشناس پیادهسازی شده است. متد آن، [apply]، سن شخص ارسالشده به عنوان پارامتر اول را که با مقدار پارامتر دوم افزایش یافته است، برمیگرداند؛
- خطوط 21–23: رابط BiFunction<Person, Integer, Double> با استفاده از یک تابع لامبدا پیادهسازی شده است. متد [apply] وزن شخص ارسال شده به عنوان پارامتر اول را که با مقدار پارامتر دوم افزایش یافته است، برمیگرداند؛
- خطوط ۲۵–۲۷: پیادهسازی [biFunctionAge] بر روی افراد اعمال میشود؛
- خطوط ۲۹–۳۱: پیادهسازی [biFunctionPoids] بر روی افراد اعمال میشود؛
نتایج بهدستآمده به شرح زیر است:
age de jean = 120
age de marie = 110
age de camille = 130
--------
poids de jean = 270.0
poids de marie = 230.0
poids de camille = 255.0
علاوه بر توابع لامبدا، جاوا ۸ نوع Stream<T> را معرفی کرد که یک جریان از عناصر با نوع T را مدل میکند. این عناصر میتوانند تحت تبدیلات متوالی که توسط توابع لامبدا پیادهسازی میشوند، قرار گیرند. در صورت امکان و در مواقعی که چندین پردازنده وجود دارد، این تبدیلات گاهی میتوانند بهصورت موازی انجام شوند.
4.7. مثال-۰۷ – رابط تابعی Supplier<T>
![]() |
رابط تابعی Supplier**<T>** به صورت زیر تعریف شده است:

تنها متد این رابط امضای T </span>**<span style="color: #000000">get()** را دارد که وظیفهاش بازگرداندن یک شی از نوع T است.
ما این رابط تابعی را با کد زیر نشان خواهیم داد:
package dvp.java8.lambdas;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple07 {
public static void main(String[] args) {
// پیادهسازی ناشناس
Supplier<Personne> supplier = new Supplier<Personne>() {
// فهرست افراد
List<Personne> personnes = Personnes.get();
//پیادهسازی رابط
@Override
public Personne get() {
int i = new Random().nextInt(personnes.size());
return personnes.get(i);
}
};
// ارزیابی
for (int i = 0; i < 5; i++) {
affiche(supplier);
}
}
//نمایش شخص
public static void affiche(Supplier<Personne> supplier) {
System.out.println(supplier.get());
}
}
- خطوط ۱۳–۲۸: پیادهسازی یک نوع Supplier<Personne>;
- خطوط ۳۱–۳۳: متد استاتیک [affiche] منتظر یک پارامتر از نوع Supplier<Personne> است؛
- خطوط ۲۵–۲۷: اجرای نمونه Supplier<Personne>;
نتایج زیر بهدست آمد:






