Ameryki nie odkrywam bo Java 8 weszła już jakiś czas temu, ale dla odświeżenia informacji i przypomnienia ogólnie co wprowadzono. Za jakiś czas ma wejść Java 9 (terminu jak wiadomo 😀 nie ma co dokładnie określać), gdzie główny nacisk ma być położny na modułowość całego środowiska uruchomieniowego.

A więc co takiego pojawiło się w javie 8:

Interfejsy funkcyjne

Interfejs funkcyjny, w javie 8 to taki interfejs który posiada definicję tylko jednej metody abstrakcyjnej, używany jest gównie w lambdach. Interfejs funkcyjny może posiadać również metodę domyślną. Interfejs funkcyjny warto opisać adnotacją „@FunctionalInterface”, wtedy kompilator będzie pilnował czy nie dodajemy do interfejsu innej deklaracji metody. Podobne do tych co są w bibliotekach Guava i Apache Commons Collections

Przykład definicji:

@FunctionalInterface
interface CompareFunctional<T>{
	boolean compare(T compare);
}

Użycie:

String pierwszy = "Pierwszy";
CompareFunctional<String> compare1 = compare -> pierwszy.equals("Pierwszy");
CompareFunctional<String> compare2 = compare -> pierwszy.equals("Drugi");
 
System.out.println("Compare: " + compare1.compare(pierwszy) + " vs " + compare2.compare(pierwszy));

Wbudowane interfejsy funkcyjne:

W javie 8 pojawiło się wiele wbudowanych interfejsów funkcyjnych, które definiują standardowe operacje w lambdach:

  • Predicate<T> – jednoargumentowa funkcja służąca do łączenia wyrażeń logicznych. Interfejs zawiera metody domyślne pozwalające na operacje logiczne: or, and, negate, isEqual
  • Function<T, R> – przyjmuje argument i zwraca wynik. Można operacje połączyć w łańcuchy przy pomocy metod domyślnych: compose, andThen, identity
  • Supplier<T> – prosty interfejs bezargumentowy zwracający wynik o określonym typie.
  • Consumer<T> – operacja na argumencie bez zwracania wyniku. Interfejs zawiera domyślną metodę: andThen
  • Comparator<T> – wykonuje standardowe operacje porównywania znane z poprzednich wersji java. W java 8 został rozszerzony o metody domyślne: reversed, thenComparing, thenComparing… (Int|Long|Double), reverseOrder, naturalOrder, nullsFirst, nullsLast
  • BiFunction<T, U, R> – interfejs przyjmujący dwa argumety i zwracający wynik.
  • definicje dla typów prostych double, int, long np.: LongFunction<R>, LongPredicate, itd.
  • konwertery: ToIntFunction<T>, ToLongBiFunction<T, U>, itd.
  • oraz inne, warto przejrzeć pakiet: java.util.function

Metody domyślne

Interfejsy mogą w Javie 8 zawierać w sobie nie tylko definicję metod czy stałe, ale również w nowej odsłonie javy tzw. metody domyślne. Są to domyślne metody, które mogą być przesłonięte przez metody implementujące dany interfejs, ale nie muszą. Mamy tutaj namiastkę wielodziedziczenia i rodzi się pytanie „Co w przypadku gdy dwie klasy implementują interfejsy które mają takie same metody domyślne?”. Nic, kod się nie skompiluje, w sumie to bardzo logiczne, bo kompilator nie wie o co nam chodzi.

Przykład – interfejsy funkcyjne zawierają dużo metod domyślnych warto je przeglądnąć, i taki sobie przykład:

@FunctionalInterface
interface CompareFunctional&lt;T&gt;{
 
	boolean compare(T compare);
 
	default boolean compareTo3(T compare){
		return compare.equals("Trzy");
	}
}

Wartości opcjonalne

Optional – to klasa kontenerowa pozwalająca uniknąć wyjątku NullPointerException. Przechowywuje ona wartość lub null oraz pozwala sprawdzić czy kontener zawiera wartość oraz porównać ją. Optional jest używany do pracy ze strumieniami, gdzie zamiast null’a można zwrócić klasę opakowywującą.

Optional&lt;Integer&gt; optional = Optional.ofNullable(null);
 
Optional&lt;Integer&gt; optional2 = Optional.ofNullable(5555);
 
System.out.println("Optional isPresent: " + optional.isPresent() + " value: " + (optional.isPresent() ? optional.get() : ""));
 
System.out.println("Optional isPresent: " + optional2.isPresent() + " value: " + (optional2.isPresent() ? optional2.get() : ""));

Referencje do metod statycznych i konstruktorów (zjechali z PHPca :P)

Teraz dostęp do metod statycznych może być realizowany przy pomocy dostępu „::”, jest to używane np. w strumieniach przy operacji mapowania przykład:

Arrays.asList("Polska", "Niemcy", "Włochy")
	.stream()
	.map(String::toCharArray)
	.map(Arrays::toString)
	.forEach(System.out::println);

Otrzymujemy wyświetlone tablice znaków:

[P, o, l, s, k, a]
[N, i, e, m, c, y]
[W, ł, o, c, h, y]

Strumienie

W javie 8 strumienie to nie tylko strumienie plikowe, ale głównie strumienie danych. Strumienie można sobie najprościej wyobrazić jako potok, rzekę danych na których wykonujemy operacje. Możemy je filtrować, sortować, przekształcać jedne dane w inne, itd. Więcej o: Strumieniach w javie 8

Przykład:

//
// Zliczmy ilość liczb większych od 20
//
final List&lt;Integer&gt; integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 29, 39, 43, 43, 23, 43, 54, 23);
 
final long count = integerList
	.stream()
	.filter(integer -&gt; integer &gt; 20)
	.count();
 
System.out.println("Count: " + count);

Lambdy

Po pierwsze trzeba było by odpowiedzieć co tą są Lambdy?. Jest to implementacja programowania funkcyjnego, gdzie zamiast kłaść nacisk na wykonywanie czynności, które rozwiążą dany problem, kładzie się nacisk na opis problemu, który prowadzi do rozwiązania. Więcej o: Lambdy w Javie 8

Interpreter Nashorn

Nashorn to nowy interpreter JavaScript, który zastąpił stary silnik Rhino. Umożliwia on łączenie kodu JavaScript z kodem Javy. Przy pomocy Nashorma kod JavaSrcipt jest kompilowany do bytecodu javy.

Przykład:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
 
try{
	engine.eval("print ('Form JavaScript');");
	engine.eval("var someValue=100.39; print ('Value: '+someValue);");
} catch (ScriptException e){
	e.printStackTrace();
}

Api daty i czasu

W jave 8 operacje na dacie zostały trochę uproszczone w działaniu, nowe api jest wzorowane na bibliotece Joda-Time. Generalnie klasy nowego Api czasu sa immutable, oferuje strefy czasowe, klasy zachowywują się zgodnie z oczekiwaniami. Zachowana jest wsteczna kompatybilność, więc można naprzemiennie używać nowego i starego API.

Przykład:

Instant birthDate = Instant.parse("1980-11-11T10:00:00Z");
Instant currentDate = Instant.now();
 
Duration daysOfLife = Duration.between(birthDate, currentDate);
System.out.print("Ilość przeżytych dni: " + daysOfLife.toDays());