Nowe funkcje dodane w Java 8
Wersja 8 została wydana 18 marca 2014 roku. Wersja do pobrania JDK8.
Zasadnicze zmiany, które weszły wraz z Java 8:
Kolejnym krokiem jest przypisania nowo utworzonego predykatu do zmiennej np:
Na koniec możemy użyć zmienną isEven w ten sposób:
Doszliśmy więc do tytułu pierwszego działu. Teraz przedstawię składnię wyrażenia lambda:
I tak w przypadku gdy jest po lewej stronie tylko jeden argument możemy nie pisać nawiasów:
W przypadku używania wyrażenia lambda nie musimy jawnie definiować typów argumentów funkcji. Możliwe jest to dzięki temu, że wyrażenia lambda można używać tylko w miejscach gdzie oczekiwania jest implementacja interfejsu funkcjonalnego. Na tej podstawie Java może wnioskować typy przyjmowane przez interfejs oraz wartości jakie zwróci.
Na koniec można jeszcze zrezygnować z nazwy metody. Nasza nowo utworzona metoda jest w anonimowa:
interface SampleInterf {
default void main() {
log.debug("default method");
}
}
implementacja interfejsu:
class Test implements SampleInterf {
public static void main(String[] args) {
Test t = new Test();
t.main();
}
}
Deklaracja interfejsu SampleInterfWithStaticMethod z metodą statyczną. Metodę tego typu możesz używać w praktyce jako klasę narzędziową w pakiecie (utility class). Takiej metody statycznej nie możesz również nadpisywać.
public interface SampleInterfWithStaticMethod {
public static void m() {
System.out.println("interface with static method");
}
}
Stream s = c.stream();
Jeżeli mamy utworzony Stream możemy nad nim pracować w 2 krokach:
src:
Zasadnicze zmiany, które weszły wraz z Java 8:
- Wyrażenia lambda
- Interfejs funkcjonalny (functional interface)
- Domyślne i statyczne metody w interfejsach
- Interfejsy funkcjonalne dla:
- Predicate
- Function
- Consumer
- Supplier
- Referencje metod i konstruktorów (method reference, constructor reference) tworzone za przy użyciu operatora " :: "
- Stream API
- Date and time API
W artykule szczegółowo opisuje każdy z nich.
in progress
1. Wyrażenia lambda
W Java jest to popularna nazwa dla funkcji anonimowej (Anonymous function) czyli takiej, która nie posiada nazwy, modyfikatora dostępu. Jest definiowana w klasie, która posiada dokładnie jedną instancję.Na początku zacznę od klasy anonimowej i przybliżę jej działanie. Następnie pokażę jak przejść z klasy anonimowej do funkcji anonimowej. Zarówno klasa jak i funkcja anonimowa są dla siebie równoważne i różnią się jedynie użytą składnią. Gdzie klasa anonimowa jest w rzeczywistości interfejsem funkcyjnym z dokładnie jedną abstrakcyjną metodą publiczną. A funkcja anonimowa to obiekt z dokładnie jedną metodą publiczną.
Klasy anonimowe zawsze są klasami wewnętrznymi. Nie tworzymy klasy i nie korzystamy z jego konstruktora tak jak we wcześniejszych wersjach Java. Tworzymy za to instancję interfejsu new Predicate<Integer>(). Następnie dodajemy wszystkie metody implementowanego interfejsu. Dla interfejsu Predicate będzie to jedna metoda apply np.
new Predicate<Integer>() {
public boolean apply(Integer integer) {
return (integer % 2) == 0;
} };
Predicate<Integer> isEven = new Predicate<Integer>() {
public boolean apply(Integer integer) {
return (integer % 2) == 0;
} };
W momencie dodania predykatu do zmiennej, która jest wewnątrz metody będzie ona tworzona za każdym razem przy wywołaniu metody. Jeżeli chcemy to zoptymalizować i wywoływać predykat jeden raz przy ładowaniu klasy w pamięci. Możemy zadeklarować zmienną jako static i przenieść ją poza metodę.Iterables.filter(data, isEven)
Jak przejść z klasy anonimowej do funkcji anonimowej?
W przypadku użycia IntelliJ program sam sugeruje ulepszenie kodu z stworzonej klasy anonimowej na funkcję anonimową (wyrażenia lambda):
(o1, o2) -> { o1.compareTo(o2) }
-> oddziela lewą część wyrażenia od prawej. Po lewej są zdefiniowane nazwy argumentów funkcji. Po prawej stronie jest ciało funkcji. Na prostym przykładzie funkcję w notacji matematycznej:
(x,y) → x² + y²
w Javie zapiszemy tak:
(x, y) -> x * x + y * y
Następnie powyższe wyrażenie lambda możemy uprościć pozbywając się niepotrzebnych elementów.x -> x * x
Gdy chcemy wykonać więcej operacji to dodajemy klamry do wyrażenia lambda:
(x, y) -> {
System.out.println("Squaring " + x + " and " + y);
return x * x + y * y;
}
Referencja do metody
Funkcja anonimowa (integer) -> isEven.test(integer) jest równoznacza ze składnią isEven::test, gdzie isEven jest nazwą zmiennej, do której przypisana została metoda, którą chcemy wywołać.List<Integer> data = new LinkedList<>(Arrays.asList(3, 1, 6, 2, 9, 0, 5, 4, 8, 7));
Predicate<Integer> isEven = (integer) -> integer % 2 == 0;
System.out.println(Iterables.filter(data, isEven::test));
List<Integer> data = new LinkedList<>(Arrays.asList(3, 1, 6, 2, 9, 0, 5, 4, 8, 7));
System.out.println(Iterables.filter(data, integer -> integer % 2 == 0));
2. Interfejs funkcjonalny (functional interface)
Przykładem zastosowania jest metoda forEach()
Aktualnie gdy chcesz korzystać z kolekcji tworzysz Iterator, którego zadaniem jest przejście po wszystkich elementach kolekcji. Pracujesz więc na poziomie logiki biznesowej, jest to kod bardziej zrozumiały.
forEach() method in Iterable interface default and static methods in Interfaces Functional Interfaces and Lambda Expressions Java Stream API for Bulk Data Operations on Collections Java Time API Collection API improvements Concurrency API improvements Java IO improvements Miscellaneous Core API improvements
3. Domyślne i statyczne metody w interfejsach
Deklaracja interfejsu "SampleInterf" z zaimplementowaną metodą domyślną:interface SampleInterf {
default void main() {
log.debug("default method");
}
}
implementacja interfejsu:
class Test implements SampleInterf {
public static void main(String[] args) {
Test t = new Test();
t.main();
}
}
Deklaracja interfejsu SampleInterfWithStaticMethod z metodą statyczną. Metodę tego typu możesz używać w praktyce jako klasę narzędziową w pakiecie (utility class). Takiej metody statycznej nie możesz również nadpisywać.
public interface SampleInterfWithStaticMethod {
public static void m() {
System.out.println("interface with static method");
}
}
4. Interfejsy funkcjonalne
Zostały opisane w artykule: https://adamtrojnar.blogspot.com/2020/03/interfejsy-funkcjonalne-wprowadzone-w.html#more
6. Stream API
Stream jest interfejsem z pakietu java.util.stream. Może przetwarzać każdy obiekt kolekcji (ArrayList, LinkedList, Vector, Stack, PriorityQueue, ArrayDeque, HashSet, LinkedHashSet, TreeSet). Tworzenie streamu wygląda następująco:Stream s = c.stream();
Jeżeli mamy utworzony Stream możemy nad nim pracować w 2 krokach:
- Konfiguracja
- Filtrowanie (.filter) w przypadku gdy chcemy wybrać interesujące nas pod elementy
- Mapowanie (.map) w przypadku gdy przetwarzamy elementy w Streamie (np. dodajemy)
- Przetwarzanie
src:
Komentarze
Prześlij komentarz