
W poprzednim wpisie wyjaśnialiśmy sobie czym tak właściwie jest ta lambda w Javie i wyszło nam, że to taka na szybko stworzona metoda w na szybko stworzonej klasie w oparciu o jakiś interfejs funkcyjny. Ale co gdyby – czysto hipotetycznie! – owa potrzebna nam metoda już gdzieś w jakiejś klasie istniała i nie trzeba byłoby jej pisać od zera?
Zacznijmy od czegoś tak prostego jak tylko się da. Czegoś, co uczymy się już pierwszego dnia naszej przygody z Javą (i chyba z każdym innym językiem programowania, tylko w mniej lub bardziej zmienionej postaci):
System.out.println(„Hello world!”);
Co robi ten kawałek? Wiadomo – wyświetla napis w nawiasach: Hello world! bezpośrednio w konsoli. Ale jak dokładnie to się dzieje?
- Co to jest ten „System”?!
- Co to jest ten „out”?!!
- Co to jest ta funkcja „println()”?!!!

System, to klasa, która nie tworzy żadnych obiektów (jej konstruktor jest prywatny), za to posiada szereg statycznych metod, które dotyczną nie tyle samej aplikacji, którą budujemy, co systemu – platformy na której się ona znajduje. Dla przykładu:
- System.currentTimeMillis() wyświetla ilość milisekund która upłynęła od „daty zero” (1 stycznia 1970, data uważana powszechnie za początek ery systemów unixowych)
- System.gc() z kolei ma w założeniu uruchamiać „odśmiecarkę” JVM (ang. „garbage collector”), ale, że GC ma własne widzimisię to de facto – ta metoda nie robi nic
- System.exit(0) kończy działanie aplikacji (dla wartości 0 kończy naturalnie, a dla 1 zupełnie jakby wyskoczył jakiś błąd)

Klasa System ma również kilka statycznych zmiennych odnoszących się do konkretnych obiektów z których metod możemy korzystać:
- System.out.println() operuje na obiekcie „PrintStream out” dzięki któremu możemy wypisać dowolny tekst w konsoli
- System.err.println() działa podobnie jak System.out.println() ale służy do wyświetlania błędów, więc jej wyniki są zawsze w kolorze czerwonym
- System.in to obiekt typu InputStream – uruchamiany jest strumień wejściowy w konsoli, ale inny obiekt musi go jakoś obsłużyć, np. Scanner
PrintStream printStream = new PrintStream(System.out);
printStream.println("Tak również wyświetlimy tekst w konsoli");
Scanner scanner = new Scanner(System.in); //a tak otworzymy strumień wejściowy
int i = scanner.nextInt(); //i przypiszemy wprowadzoną liczbę do zmiennej i
Dobrze, skoro już jest jasne jak działa popularny System.out.println() to teraz zastanówmy się czym do cholery jest poniższy kawałek:
System.out::println

Jak widać na obrazku powyżej, jest to coś powiązane z lambdą, ale inne – jeszcze krótsze. Zaś efekt działania pozostaje ten sam – tu i tam tekst wyświetla się w konsoli.
Wróćmy do kroków niezbędnych do utworzenia lambdy z poprzedniego odcinka i dodajemy do tego ten nowy kawałek z podwójnym dwukropkiem:

Wyraźnie widać, że początek jest ten sam. Robimy klasę anonimową, a potem po kolei usuwamy zbędne elementy aż zostaje „goła” lambda. Ale czasami, tylko w sprzyjających warunkach, możemy pójść inną drogą – odnosząc się do już gotowej metody.
A jakie warunki są „sprzyjające”?
Po pierwsze musimy wybrać taką metodę która jest bliźniaczo podobna do metody zadeklarowanej w interfejsie z którego korzystamy. O ile nazwy mogą się różnić to już:
- typ zwracany MUSI być ten sam
- liczba użytych parametrów MUSI się zgadzać
- typy użytych parametrów MUSZĄ być dokładnie takie same
- no i oczywiście ta metoda musi robić to co nam akurat potrzeba

Największym problemem w zrozumieniu jak działa method reference jest to, że nie widać tam żadnych zmiennych. Nawet w lambdzie, pierwszym co robimy jest stworzenie sobie na szybko jakiejś zmiennej:
Interface interface = zmienna -> zmienna.jakaśMetoda();
interface.metodaInterfejsu(tuWrzucamyParametrZmienna);
Pamiętajmy na jakiej zasadzie działa wyrażenie lambda – ta tymczasowo stworzona zmienna jest pozbawiona typu tylko i wyłącznie dlatego, że jest on kopiowany z metody interfejsu. Odniesienia działają w ten sam sposób, tylko, że jeszcze bardziej.
Kiedy wpiszemy np. String::trim, czyli metodę klasy String, która pobiera jeden ciąg znaków, usuwa z niego spacje z przodu i z tyłu, a następnie go zwraca, JVM sprawdza, czy metoda naszego interfejsu typującego ma podobną sygnaturę:
- zwraca String? zgadza się!
- ma jeden parametr? zgadza się!
- ten parametr jest typu String? zgadza się!
Wszystko ok, można użyć odniesienia do metody. Albo przeciwnie – coś nie bangla i trzeba jak jaskiniowiec korzystać ze zwykłej lambdy.
Kiedy już nauczymy się tworzyć odniesienia do metod, nasz kod stanie się jeszcze bardziej zwięzły – nie dość, że będzie krótszy to jeszcze od razu będzie widać co tam się dzieje.
Interface interface = String::trim;
interface(tuWrzucamyJakiśString);


forEach()
Odniesienia do metod są bardzo często spotykane w Stream API (o tym w kolejnym odcinku) oraz w metodzie forEach() która jest estetycznym zamiennikiem zwykłego iterowania po elementach kolekcji/tablicy znanego z Javy 5.
Wcześniej trzeba było podać typ elementów kolekcji, zadeklarować zmienną i podać nazwę kolekcji w której będziemy przebierać. Następnie blok {} i możemy w końcu wrzucać elementy do pasujących nam metod. Sporo tego, szczególnie jeśli chcemy tam użyć tylko jednej metody. Z pomocą przychodzi właśnie funkcja forEach sparowana z method reference:

Teoria teorią, ale jak to ogarnąć na co dzień?
Wyobraźmy sobie przeciętnego człowieka: głowa, 2 ręce, 2 nogi, brzuch, tyłek i tak dalej. Stwórzmy sobie obiekt takiego człowieka (Person) w kodzie, niech jego klasa implementuje interfejs Living z jedną metodą do() czyli „zrób”:
Living person = new Person();
person.do();
Podsumujmy: mamy człowieka i chcemy, żeby coś zrobił. Używając normalnej lambdy musielibyśmy za każdym razem pisać co dokładnie chcemy żeby gościu wykonał. Ale korzystając z odniesienia do (już istniejącej) metody wystarczy, że podamy klasę gdzie ta metoda się znajduje i jej nazwę:
Living p1 = Frog::jump;
Living p2 = Cook::prepareDinner;
Living p3 = Sleep::haveNap;
Living p4 = Animal::eat;
Living p5 = Rock::standStill;
Living p6 = Job::work;
Living p7 = Rezus::gibaj;

Tak jak z każdym nowym skillem, kluczem do wprawy jest regularna praktyka, ale tym zajmiemy się w następnym odcinku – poświęconym strumieniom, czyli zagadnieniu gdzie lambdy i odniesienia pojawiają się w ilościach hurtowych.
Na dzisiaj tyle, kod z tego odcinka znajdziecie na moim GitHubie.