Lambda a vnitřní anonymní třídy
Velmi se nám žádá říci, že lambda výrazy jsou jen zkratky jak napsat vnitřní anonymní třídy. Ale pamatuj si, není tomu tak. Vypadá to podobně, ale lambda není implementace rozhraní. Lambda je sama osobě nezávislá jiná věc.
Podívejme se na příklad. Namísto toho, abychom použili implementační třídu našeho rozhraní IHelloWord, vytvoříme si vnitřní anonymní třídu.
IHelloWord helloWord3 = new IHelloWord() {
@Override
public void sayHello() {
System.out.println("HelloWord impls inner anonymous class");
}
};
Všechny 3 možnosti, které mají jako návratovou hodnotu rozhraní IHelloWord můžeme podsunout do metody printHelloWord(IHelloWord helloWord).
helloWord.printHelloWord(helloWord1);
helloWord.printHelloWord(helloWord2);
helloWord.printHelloWord(helloWord3);
Jak to, jak to?
Jak java ví, jaký má použít typ pro lamba výraz? Abychom tomu porozuměli, vytvoříme si novou třídu, kde budeme pracovat s lambda výrazem.
Vytvořme si rozhraní, které bude mít jednu metodu, která bude vracet int a na vstupu bude také int.
interface Nasob{
int nasob(int a);
}
Ako by vyerala implementácia tohto rozhrania?
class NasobPiatimi implements Nasob{
public int nasob(int a){
return a*5;
}
}
Nyní si navrhněme lambda výraz, který odpovídá dané metodě. Nepotřebujeme návratovou hodnotu int, neboť java umí na ni přijít sama a nepotřebujeme ani název metody a ani modifikátor přístupu public. Náš lambda výraz bude vypadat takto:
(int a) -> a*5;
Nyní použijte tento lambda výraz:
public static void main(String[] args) {
Nasob nasobPiatimi = (int a) -> a*5;
System.out.println(nasobPiatimi.nasob(10));
}
Na výstupu bude 50. V tomto příkladu se lambda tváří jako instance rozhraní Nasob. V předchozích příkladech, kdy jsme používali HelloWord, jsme takovou proměnnou vkládali jako parametr metody printHelloWord (HelloWord3 v IDEi). Namísto toho jsme mohli tuto lambdu vložit přímo do metody.
helloWord.printHelloWord(() -> System.out.println("HelloWord impls lambda"););
Java kompilátor vezme tento lambda výraz a podívá se kam jde. Jedná se o metody printHelloWord a podívá se, co akceptuje na vstupu. Akceptuje rozhraní HelloWord. Pokud lambda sedí s požadavkem, že dané rozhraní obsahuje jen jednu metodu a ta vrací void a na vstupu nemá žádný parametr, tak java řekne, že daná lambda je typu HelloWord. Toto se jmenuje Type inference. Java si sama zjistí typ. Teď, když víš jak java dokáže zjistit typy, vrátíme se k příkladu, který jsme začali psát v této kapitole. V našem příkladu umíme ještě více zkrátit zápis našeho lambda výrazu.
Nasob nasobPiatimi = (int a) -> a*5;
System.out.println(nasobPiatimi.nasob(10));
Jelikož naše lamba jde do metody rozhraní, kterou známe
interface Nasob{
int nasob(int a);
}
Tak vieme presne povedať aký typ má vstupný paramter metódy. Je to int.
interface Nasob{
int nasob(int a);
}
Když to víme, tak nemusíme při psaní lambda výrazu znovu specifikovat typ vstupního parametru.
Nasob nasobPiatimi = (a) -> a*5;
A jelikož máme jen jeden parametr, nemusíme psát ani závorky.
Násob napětí = a -> a*5;
Už nebudeme nic mazat, neboť by nám už nic nezbylo 😃
Nyní můžeme napsat metodu, která bude na vstupu očekávat rozhraní Nasob a když ji použijeme, tak do ní vložíme na vstup náš lambda výraz.
public static void printNasob(Nasob nasob){
System.out.println(nasob.nasob(10));
}
public static void main(String[] args) {
printNasob(a -> a*5);
}
V jevu mohli klidně vytvořit nový typ pro tyto lambda výrazy. Ale neudělali to a jedním z důvodů je i zpětná kompatibilita se starším kódem. Jak už víme, tak lambda výrazy můžeme použít všude tam, kde máme vyhovující rozhraní.
Ve vnitřních anonymních třídách, v metodách kde je na vstupu interface a podobně. Příklad:
HelloWord helloWord3 = new HelloWord() {
@Override
public void sayHello() {
System.out.println("HelloWord impls inner anonymous class");
}
};
HelloWord helloWord3 = () -> System.out.println("HelloWord impls inner anonymous class");
Při tomto musíme pamatovat, aby rozhraní byla jedno metodová nebo aby ostatní metody rozhraní byly default. A dané metody v rozhraních, aby se shodovaly s lambda výrazem. Takové rozhraní s jednou abstraktní metodou (metoda, která poskytuje popis ne implementaci) se nazývá Functional interface.
Představ si, že používáš rozhraní, které má jen jednu metodu a používáš ho pro lambda výrazy. Nyní by někdo cizí přišel a do tohoto rozhraní by přidal další abstraktní metodu, přesněji její popis bez implementace. Takové rozhraní by již více nebylo functional interface a proto by se nemohlo použít pro lambda výraz a nastala by chyba - přestože rozhraní by bylo v pořádku. Třeba na to myslet a pokud chceme něco přidat do functional interfac, tak jen jako default metody.
Abychom upozornili kohokoli, kdo by chtěl něco přidat do našeho rozhraní, tak máme možnost přidat anotaci @FunctionalInterface. K anotacím se ještě dostaneme, tak se nebojte. Nyní je důležité vědět, že je to pomůcka – tato pomůcka nám udělá to, že jakmile napíšeme další metodu do našeho rozhraní, tak nastane chyba. Danou anotaci nemusíme psát, ale je to super.
@FunctionalInterface
public interface HelloWord {
void sayHello();
}
Příklady na vyzkoušení:
- vytvoř si seznam míst
- setřiď seznam
- napiš metodu, která vypíše vše ze seznamu míst
- udělej si metodu, která vypíše jen ta města, která se skládají z jednoho slova nepoužívej při tom lambda výrazy
Pokračování příště 👋
Články a online kurzy o Javě pro tebe připravuje
Jaro Beňo.