ś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 ;-)
Referencje w Java'ie

piątek, czerwca 01, 2012

Maszyna stanów kontra labirynt ;-)

OK, wiem że to co tu napiszę nie zawsze działa ;-) [przed chwilą stworzyłem labirynt w którym moja maszyna stanów nie podołała ;-) ale już jest za późna pora by szukać błędu...]

Zobaczyłem dziś na HackerNews linkę do Google Blockly, a tam jeden z przykładów [do napisania ;-)] to rozwiązywanie labiryntu.
Chwilę się tym pobawiłem, ale mi ELSE brakowało w IF, więc sięgnąłem po kawałek papieru (tym razem koperta z banku) i narysowałem sobie maszynę stanów, która jak mi się wydaje powinna rozwiązywać labirynt przez poruszanie się po nim z lewą ręką na ścianie..

Później to zakodowałem w Pythonie i tak wygląda program w działaniu ;-)


Najważniejszy kawałek kodu wygląda tak:

while (startX!=endX) or (startY!=endY):
if state==0:
newDir = turnLeft(dir)
if empty(startX+newDir[0],startY+newDir[1]):
state=1
else:
state=2
continue
if state==1:
dir = turnLeft(dir)
startX+=dir[0]
startY+=dir[1]
state = 0
continue
if state==2:
if empty(startX+dir[0],startY+dir[1]):
state=3
else:
state=4
continue
if state==3:
startX+=dir[0]
startY+=dir[1]
state=0
continue
if state==4:
dir = turnLeft(dir)
state=0
continue

Na wejściu mamy w (startX,startY) pozycję startową, a w (endX,endY) pozycję końcową.
turnLeft() po prostu obraca "wektor" dir w lewo o 90 stopni, a empty() sprawdza czy pole przed nami jest puste, czy nie :-)
state to akutalny stan maszyny, można to zrobić ładniej, ale chciałem by było to widać.

Jak ktoś ciekawy to tutaj pliki do pobrania:
maze.py - program w Pythonie (jeśli chcesz go uruchomić na Linuksie czy innym *niksie to zmień os.system("cls") w os.system("clear"), nie chciało mi się kombinować to użyłem tak ohydnej metody czyszczenia ekranu :-))
maze.txt - labirynt
maze2.txt - labirynt zabójca dla programu ;-) zaczyna się kręcić "jak smród po gaciach" ;-)

I moje 30 minut kodowania (prywatnego) odbębnione :-)


Podobne postybeta
Nigdy nie zapominaj o FSM! ;-)
Z NMEA do KML (chyba najbardziej kryptyczny tytuł postu jak stworzyłem ;-))
Śmierdząca ryba wolności ;-)
Windows mnie jednak nie lubi ;-)
Zdradliwa Java i 8 królowych ;-)