środa, czerwca 27, 2012

Java 8 nadchodzi....

Ostatnio na HackerNews natrafiłem na linkę do opisu paru nowych rzeczy z Java 8.
Postanowiłem się im przyjrzeć i mam mieszane uczucia ;-)

Lambdy nie są do końca tym czego się spodziewałem. Zakładałem, że będą bardzo podobne do tego co jest w JavaScript.... aż tak dobrze jednak nie będzie ;-)
Nie przypiszemy sobie (albo ja nie umiem) ciała metody do zmiennej i nie wywołamy później, o np. tak:
Mathod doSth = () -> { System.out.println("Toster"); };
doSth();
Taki numer przejdzie tylko jeśli naszą lambodwą metodę przypiszemy do jakiegoś interfejsu funkcjonalnego (takiego z 1 metodą tylko, np. do Runnable).
Runnable r = () -> { System.out.println("Toster"); };
r.run();

To zadziała.
Ciekawe jest to, że w ciele takiego lambodwej metody możemy używać wartości zmiennych (z zewnątrz), które są efektywnie finalne ;-)
Z klasami anonimowymi było tak:
final int i = 7; // i musi być finalne by móc go użyć dalej
new Thread(new Runnable() {
   public void run() {
       System.out.println(i);
   }
}).run();
Teraz może być tak:
int i = 7;
new Thread(() -> { System.out.println(i); }).start();

pod warunkiem jednak, że i nigdzie dalej nie ulegnie zmianie ;-) nie może też ulec tej zmianie wcześniej ;-)

Faktem jest jednak, że taki kod jest krótszy i chyba jednak bardziej czytelny.

Lambdów można używać też kolekcjami i to jest naprawdę miłe.
Kiedyś (znaczy teraz też, od 2013 roku już nie będzie trzeba ;-)) by z listy wybrać tylko parzyste liczby trzeba było walnąć kod:
List<Integer> list = Arrays.asList(75,22,3,55,22,11,7,35);
List<Integer> result = new ArrayList<Integer>();
for (int num:list) {
  if (num%2==0) result.add(num);
}

Teraz wystarczy:
List<Integer> list = Arrays.asList(75,22,3,55,22,11,7,35);
List<Integer> result = list.filter(e -> e%2==0).into(new ArrayList<Integer>());
A właściwie nawet tylko:
List<Integer> list = Arrays.asList(75,22,3,55,22,11,7,35).filter(e -> e%2==0).into(new ArrayList<Integer>());


Co trzeba przyznać wygląda na krótsze ;-) i chyba czytelniejsze.

W Java 8 ma pojawić się też możliwość definiowania domyślnej implementacji kodu dla interfejsów....
W swoim życiu spotkałem się chyba z 1 sytuacją gdy to byłoby potrzebne, bez tego musieliśmy mieć w interfejsie klasę Adaptera do tego i jej używaliśmy tam gdzie to implementowaliśmy......
To taka furtka do wielodziedziczenia jest.
Wygląda to tak:
public interface I1 {
   void doSth() default {
        System.out.println("from I1");
   }
}
I od teraz jeżeli zrobimy klasę, która to implementuje, ale nie napiszemy naszej implementacji to w naszej klasie pojawi się automagicznie metoda o implementacji domyślnej...
Jeśli napiszemy własną to tamta zniknie (można się do niej dostać chyba przez I1.super()).

Zabawniej się robi jak są 2 interfejsy z tą samą metodą i domyślnymi implementacjami :-)
public interface I1 {
   void doSth() default {
        System.out.println("from I1");
   }
}

public interface I2 {
   void doSth() default {
        System.out.println("from I2");
   }
}

public class Test implements I1, I2 {
}
Taki kod się nie skompiluje.... ale jeśli Test zmienimy na:
public class Test implements I1, I2 {
   public void doSth() {
      System.out.println("from Test");
   }
}
To już się skompiluje ;-)

Ogólnie przydatne mogą być lambdy w operacjach na kolekcjach. Do składni się trzeba przyzwyczaić, ale później pozwalają dużo czasu na pisaniu zaoszczędzić.... inna sprawa, że nie jestem pewien jak później się przekładają na maintanance ;-)

Ogólnie mam mieszane uczucia, ale bardzo możliwe, że przemawia przeze mnie konserwatyzm ;-)


Podobne postybeta
Ile to jest 1+1 w Java'ie?
Sztuczki tropiciela błędów, part 4
Niecne wykorzystanie refleksji... czyli jak poszukać tekstu w drzewie obiektów? ;-)
Sztuczki tropiciela błędów, part 3 - hackujemy klasy finalne ;-)
Nie taka klasa konkretna straszna....