Ugrás a fő tartalomhoz

Lambda kifejezések

Esetenként előfordul, hogy paraméterként egy konkrét lefuttatandó kódot, függvényt szeretnénk átadni. A Java osztályalapú megközelítése, azonban nem teszi lehetővé, hogy önálló függvényeket definiáljunk.

Emiatt a függvényünket egy osztályba kell szerveznünk, amit példányosítanunk kell, vagy egy interface-t kell implementálnunk.

public interface Printable {
void print();
}

public class Main {
public static void main(String[] args) {
Printable printable = new Printable() {
@Override
public void print() {
System.out.println("asd");
}
};
}
}

Láthatjuk, hogy ez rengeteg felesleges kóddal jár. Ezen probléma megoldására vezette be a Java 8 a lambda kifejezéseket.

Egy interfészt funkcionális interfésznek nevezünk, ha egyetlen absztrakt metódussal rendelkezik.

Jelölésére a @FunctionalInterface annotációt használjuk, ami bár opcionális megadása ajánlott, mivel így a fordító ellenőrizni tudja, hogy valóban funkcionális interfészt adunk meg.

@FunctionalInterface
public interface Printable {
void print();
}
public class Main {
public static void main(String[] args) {
Printable lambda = () -> System.out.println("asd");
lambda.print() // meghívás
}
}

Általában nincs szükségünk saját funkcionális interfészek definiálására, mivel a legtöbb használati esetet a nyelv beépített interfészei lefedik.

Funkcionális interfészek

Function

A matematikából jól ismert függvény, egy bemenethez egy kimenetet rendel. Egyetlen metódusa az apply().

Function<Integer, Integer> increment = n -> n+1;
increment.apply(5);
veszély

Bár logikusnak tűnne, sajnos a Java nem engedi meg azt, hogy a függvényt egyszerűen increment(5) módon hívjuk meg.

Consumer

Olyan lambda mely egy paraméterrel rendelkezik, de nem ad vissza értéket.

Consumer<Integer> printer = n -> System.out.println(n);
printer.accept(5);
tanács

Amikor egy lambda csak egy meglévő függvényt hív meg használhatunk metódushívás helyett metódusreferenciát is.

Consumer<Integer> printer = System.out::println;

Supplier

Nincs paramétere és értéket ad vissza.

Supplier<String> abc = () -> "abc...";
String abcString = abc.get();

Runnable

Nincs paramétere, nem tér ad vissza értéket.

Runnable r = () -> System.out.println("valami");
r.run();

Predicate

Paraméterhez logikai értéket rendel.

Predicate<Integer> gt5 = n -> n > 5;
gt5.test(6);

UnaryOperator, BinaryOperator

A Function speciális esete, ahol a paraméter és a visszatérési érték típusa azonos.

UnaryOperator<Integer> negate = n -> -1 * n;
negate.apply(5);

Specializált interfészek

A Java generikus rendszere nem teszi lehetővé, hogy primitív típusok típusparaméterek legyenek. A wrapper osztályok (például Integer) használata azonban rontja a teljesítményt, ezért az előbb említett funkcionális interfészek többsége rendelkezik az int, long, és double primitív típusokra specializált változattal.

Például:

IntUnaryOperator increment2 = n -> n+1;
increment2.applyAsInt(5); // metódusnevek eltérnek

Többparaméteres interfészek

Bővített interfészek

A Function, Consumer, Predicate, és UnaryOperator mind rendelkeznek két paraméterre bővített változatokkal.

Például:

BiPredicate<Integer, Integer> greaterThan = (a, b) -> a > b;
greaterThan.test(5, 7);

BinaryOperator<Integer> add = (a, b) -> a + b;
add.apply(5, 3);

Comparator

Háromirányú (-1, 0, 1 értékeket visszaadó) összehasonlítást végez a megadott paramétereken.

Comparator<Integer> comparator = Integer::compareTo;
comparator.compare(5, 6);