W niniejszym wpisie przyjrzymy się wybranym nowościom w Java wersji 9. Pełną listę zmian wprowadzonych w Java 9 można znaleźć tutaj.
Moduły
Moduły zostały wprowadzone w celu zapewnienia większej enkapsulacji – dzięki nim możliwe jest między innymi używanie publicznych klas w ramach modułu bez obawy, że ktoś będzie mógł zaraz ich użyć. Moduły pozwalają na deklaracje tego co może być używane, a co nie. Czytamy na stronie dotyczącej modułów:

module-info.java
Moduł definiujemy w pliku module-info.java i powinien on znajdować się w katalogu przechowującym pakiety (czyli na przykład src):

Podstawowa definicja
Podstawowa definicja modułu powinna zawierać jego nazwę oraz eksportowane (możliwe do użycia przez innych) pakiety:
module Library {
exports api;
exports internal to only.this.package.can.access.internal;
}exports api powoduje eksport całego pakietu api. exports internal to only.this.package.can.access.internal powoduje eksport pakietu internal, ale tylko dla pakietu only.this.package.can.access.internal (z poziomu innych pakietów niż only.this.package.can.access.internal nie będzie możliwe użycie pakietu internal).
Wykorzystywanie innych modułów
Aby wykorzystać jakiś moduł w swoim module należy użyć opcji requires:
module OtherModule {
requires Library;
}Tranzytywne zależności
Moduły domyślnie nie dociągają tranzytywnych zależności. Należy to jawnie zaznaczyć. Przykładowo mając trzy moduły:
module Library {
exports api;
}
module ModuleUsingLibrary {
requires Library;
}
module ModuleX {
requires ModuleUsingLibrary;
}W module ModuleX nie jest możliwe użycie klas z modułu Library.
Aby to rozwiązać należy dodać słowo kluczowe transitive w module ModuleUsingLibrary:
module Library {
exports api;
}
module ModuleUsingLibrary {
requires transitive Library;
}
module ModuleX {
requires ModuleUsingLibrary;
}Od teraz możliwe jest używanie klas z modułu Library w module ModuleX.
Zależność statyczna – tylko dla kompilacji
Możliwe jest użycie słowa kluczowego static po to, żeby użyć modułu tylko w celu kompilacji. W trakcie wykonania będzie on musiał być dostarczony przez inny moduł:
module ModuleY {
requires static Library;
}W tym momencie możliwe jest użycie klas z modułu Library, ale tylko w czasie kompilacji. Przy uruchomieniu kodu dostaniemy wyjątek ClassNotFoundException . Aby pozbyć się tego błędu należy zaciągnąć moduł korzystający tranzytywnie z modułu Library, przykładowo:
module ModuleY {
requires static Library;
requires ModuleUsingLibrary;
}Klasy JDK podzielone na moduły
Klasy JDK zostały podzielone na moduły. Aby sprawdzić jakie moduły są dostępne w Javie należy wpisać polecenie:
java --list-modulesWycinek rezultatu:
java.activation@9.0.4
java.base@9.0.4
java.compiler@9.0.4
java.corba@9.0.4
java.datatransfer@9.0.4
java.desktop@9.0.4
java.instrument@9.0.4
...Kod bez pliku module-info.java
Niestety, ale kod nie posiadający definicji modułów jest kompletnie agnostyczny względem modułów – nie dotyczą go eksporty (może używać wszystkich klas, nawet tych nie ujętych w eksportach).
Prywatne metody w interfejsach
Coraz częściej wykorzystywane metody domyślne w interfejsach nakłoniły twórców Javy do wsparcia metod prywatnych w interfejsach. Dzięki temu metody domyślne nie muszą być już jednym wielkim monolitem, lecz mogą być podzielone na mniejsze metody:
public interface Person {
String name();
String surname();
int age();
default String stringRepresentation(){
return String.join(" and ", formatName(), formatSurname(), formatAge());
}
private String formatName(){
return "His/her name is " + name();
}
private String formatSurname(){
return "His/her surname is " + surname();
}
private String formatAge(){
return "His/her is " + age() + " years old";
}
}Nowe metody w strumieniach
takeWhile/dropWhile
takeWhile i dropWhile służą do pomijania elementów w strumieniu. takeWhile bierze elementy dopóki warunek jest spełniony:
Stream.of(1,2,3,4,5,4,3,2,1)
.takeWhile(e -> e < 5)
.forEach(System.out::print);
->1234Zaś dropWhile wyrzuca elementy dopóki warunek jest spełniony:
Stream.of(1,2,3,4,5,4,3,2,1)
.dropWhile(e -> e < 5)
.forEach(System.out::print);
->54321iterate
iterate pozwala na iterację podobną do klasycznej pętli for. Lecz należy tu pamiętać, że ostatni argument to funkcja, stąd nie jest możliwe użycie klasycznego wyrażenia i++, jako że nie zwraca ono zwiększonej wartości (tylko poprzednią i zwiększa indeks później). Stąd należy używać wyrażenia ++i.
Stream.iterate(1, i -> i < 12, i -> ++i)
.map(String::valueOf)
.collect(Collectors.joining(",")));
->1,2,3,4,5,6,7,8,9,10,11Odpowiednikiem w wersji klasycznej pętli for będzie:
for (int i = 1; i < 12 ; i++) {
System.out.print( i + (i+1 < 12 ? "," : ""));
}
->1,2,3,4,5,6,7,8,9,10,11ofNullable
Metoda ofNullable pozwala na otrzymanie strumienia posiadającego jeden element, jeśli jest on różny od wartości null. Jeśli jest równy wartości null, to zawartość strumienia będzie pusta:
List<Object> result = Stream.ofNullable(null)
.collect(Collectors.toList());
System.out.println(result);
->[]JShell
Jest to coś w rodzaju linii komend, gdzie możemy wprowadzać polecenia języka Java w celu na przykład szybkiego sprawdzenia dowolnego wyrażenia. Przykładowo chcąc sprawdzić długość identyfikatora:
String reallyBigStringIdentifier = "J3829J293FH230HFH320FH9FH320FH39FH320FH308FH32F";
reallyBigStringIdentifier.length();
->47Metody fabrykujące w kolekcjach
Java 9 wprowadza także przydatne metody fabrykujące dla kolekcji. Lecz należy pamiętać, że stworzone kolekcje są niemodyfikowalne.
List.of(1,2,3,4,5);
Set.of("a","b","c","d");
Map.of("a", 1, "b", 2, "c", 3);@Deprecated
Adnotacja @Deprecated dostała dwa nowe atrybuty – since oraz forRemoval. since – ma oznaczać, od której wersja funkcjonalność jest deprecjonowana. forRemoval – ma oznaczać, czy dana funkcjonalność ma zostać usunięta w najbliższych wersjach:
@Deprecated(forRemoval = true, since = "1.0.4")Warto zajrzeć
1. Pełna lista zmian wprowadzonych w Java 9
2. Projekt z przykładami z wpisu
