Samouczek Java 8
„Java wciąż żyje i ludzie zaczynają ją rozumieć”.
Witam w moim wprowadzeniu do języka Java 8. Ten artykuł poprowadzi Cię krok po kroku przez wszystkie nowe funkcje od Java 7 do Java 8. Dzięki szybkim i prostym przykładom kodu możemy dowiedzieć się, jak korzystać z domyślnych interfejsów, odwołań do
metod i Powtarzalne adnotacje . Na koniec artykułu zapoznamy się z Stream API.
Żadnych niepotrzebnych gadań – po prostu kod i komentarze! Do przodu!
Domyślne metody interfejsów
Java 8 pozwala nam dodawać nieabstrakcyjne metody (które są zaimplementowane) do interfejsów poprzez dodanie rozszerzenia
default
. Ta funkcja jest również znana jako
Metody rozszerzania . Poniżej znajduje się pierwszy przykład:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
Oprócz metody abstrakcyjnej
calculate
interfejs
Formula
definiuje także metodę domyślną
sqrt
. Klasy implementujące ten interfejs muszą jedynie zaimplementować bibliotekę
calculate
. Domyślną metodę
sqrt
można zastosować natychmiast po wyjęciu z pudełka.
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100);
formula.sqrt(16);
Interfejs
Formula
jest zaimplementowany jako klasa anonimowa. Kod jest zbędny: 6 linii do implementacji
sqrt(a * 100)
. Jak zobaczymy w następnej sekcji, istnieje ładniejszy sposób zaimplementowania pojedynczej metody w Javie 8.
Wyrażenia lambda
Zacznijmy od prostego przykładu sortowania listy ciągów w poprzednich wersjach Javy:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
Metoda statyczna
Collections.sort
przyjmuje listę i komparator w kolejności, w jakiej lista ma być posortowana. Zawsze możesz utworzyć anonimową klasę porównawczą i przekazać ją dalej. Zamiast tworzyć anonimową klasę, w Javie 8 możesz utworzyć krótszą notację, wyrażenie lambda.
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
Jak widać kod jest znacznie krótszy i łatwiejszy do odczytania. Ale można to zrobić jeszcze krócej:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
W przypadku treści z jedną linią możesz pominąć
{}
słowo
return
. Ale możesz zrobić to jeszcze krócej:
Collections.sort(names, (a, b) -> b.compareTo(a));
Kompilator Java wie o typach argumentów, więc można je również pominąć. Przyjrzyjmy się bliżej wyrażeniom lambda i zobaczmy, jak można ich używać.
Interfejsy funkcjonalne
Jak wyrażenia lambda pasują do systemu typów Java? Każda lambda odpowiada typowi opisanemu w interfejsie. Dlatego interfejs funkcjonalny powinien zawierać tylko jedną metodę abstrakcyjną. Każde wyrażenie lambda tego typu będzie odpowiadać tej abstrakcyjnej metodzie. Ponieważ metody domyślne nie są abstrakcyjne, możesz w razie potrzeby tworzyć metody domyślne w interfejsach funkcjonalnych. Możemy również używać dowolnych interfejsów jako wyrażeń lambda, jeśli w tym interfejsie istnieje tylko jedna metoda abstrakcyjna. Aby spełnić te wymagania należy dodać
@FucntionalInterface
adnotację. Kompilator o tym wie i zgłosi wyjątek, jeśli chcesz podać więcej niż jedną metodę abstrakcyjną. Przykład:
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);
Należy pamiętać, że kod zostanie również skompilowany, jeśli
@FunctionalInterface
pominie się adnotację.
Odniesienia do metod i konstruktorów
Powyższy przykład można również zmniejszyć, używając odwołań do metod:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);
Java 8 umożliwia przekazywanie referencji do metody lub konstruktora poprzez dodanie
::
. Powyższy przykład pokazuje, jak możemy odwoływać się do metody statycznej, chociaż możemy również odwoływać się do metod niestatycznych:
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);
Zobaczmy jak
::
to działa z konstruktorami. Na początek zdefiniujemy przykładową klasę z różnymi konstruktorami:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
Następnie zdefiniujemy interfejs do tworzenia nowych obiektów:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
Zamiast wdrażać fabrykę kreacji, połączymy to wszystko w jedną całość przy
::
pomocy konstruktora:
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
Stworzyliśmy link do konstruktora poprzez
Person::new
. Kompilator Java automatycznie wybierze poprawny konstruktor pasujący do sygnatury metody
PersonFactory.create
. ... Ciąg dalszy nastąpi. Niestety nie znalazłem sposobu na zapisanie wersji roboczej artykułu, a to jest naprawdę dziwne, a czas na tłumaczenie się skończył - więc dokończę później. Dla każdego, kto zna i rozumie język angielski –
artykuł oryginalny . Jeśli masz sugestie dotyczące poprawienia tłumaczenia, napisz w dowolny dostępny Ci sposób.
Moje konto na Githubie
GO TO FULL VERSION