Zmiany w Javie od wersji 11 do 17

Zmiany w Javie od wersji 11 do Java 17

To kolejny artykuł opisujący zmiany języka Java pomiędzy wersjami LTS (Long term support). W poprzednim opisywałem zmiany pomiędzy Javą 8 i Javą 11. W tym artykule opiszę najważniejsze zmiany, które zaszły od wprowadzenia Javy 11, aż do ostatnio wydanej wersji, czyli Javy 17.

Java 12

Wyrażenia switch (Switch Expressions)

Jedną z głównych zmian w języku w wersji 12 były wyrażenia switch (Switch Expressions), w tej wersji wchodziły jako zapowiedź, ale ostatecznie miały swoją premierę w Javie 14.

Normalny switch:

switch (month) { 
  case 1: System.out.println("Styczeń"); 
    break; 
  case 2: System.out.println("Luty"); 
    break; 
  case 3: System.out.println("Marzec"); 
    break; 
}

Java 12, switch expression:

switch (month) { 
  case 1 -> System.out.println("Styczeń"); 
  case 2 -> System.out.println("Luty"); 
  case 3 -> System.out.println("Marzec"); 
}

W Javie 13 gdzie miała miejsce, druga zapowiedź Switch expression, dodano możliwość zwracania wartości poprzez słowo kluczowe yield (to taki break z możliwością zwrócenia wyniku).

Wyrażenia switch były przymiarką do Pattern Matching w Javie, który powoli był wprowadzany w kolejnych wersjach.

Teeing Collector

Poza switch expression było jeszcze kilka drobnych zmian w api. Dodano nowy kolektor w klasie Collectors, jest to teeing  kolektor, który przyjmuje jako parametry dwa kolektory i funkcję (BiFunction) i działa on mniej więcej tak:

double average = Stream.of(1, 2, 3, 4, 5, 6, 7)
  .collect(
    teeing(
      summingDouble(i -> i), 
      counting(), 
      (sum, n) -> sum / n
    )
);

CompactNumberFormat

Została dodana także nowa klasa formatująca CompactNumberFormat, która zapisuje duże liczby w skróconym formacie np. 1 000 000 zamienia na 1M.

Java 13

Text blocks

W tej wersji miała też miejsce druga zapowiedź switch expression, które opisałem wyżej. A kolejną funkcjonalnością, która miała swoją pierwszą zapowiedź były bloki tekstu (Text blocks), które ostatecznie została włączona do Javy w wersji 15. Bloki tekstu to inaczej wielolinijkowe stringi, z tą różnicą, że teraz można je zapisać w jednym bloku, bez konieczności użycia konkatencji i znaków nowej linii (\n).

Stara wersja:

String html = "<html>\n" + 
              " <body>\n" + 
              " <p>Hello, world</p>\n" + 
              " </body>\n" + "</html>\n";

W Java 13:

String html = """ 
                  <html> 
                    <body>
                      <p>Hello, world</p>
                    </body>
                  </html>
              """;

Dodane zostały także trzy metody do klasy Stringzwiązane właśnie z tą funkcjonalnością. Są to metody:

  • formatted() – formatuje stringa używając tego samego stringa jako formatu, jest to ekwiwalent dla wywołania format(this, args)
  • stripIndent() – usuwa nadmiarowe spacej z bloku
  • translateEscapes() – zwraca stringa ze znakami eskejpującymi zakodowanymi w Unicode

Java 14

Klasy Record

W tej wersji swoje zapowiedzi miały Rekordy, które ostatecznie weszły w wersji 16. Rekordy to nowy typ klas, dzięki którym zmniejszy się ilość kodu szablonowego (boilerplate code). Rekordy posiadają bowiem niejawne metody takie jak gettery, konstruktory, equals, hashcode, toString. W rekordach nie musimy ich implementować, a możemy ich używać.

Rekordy omówiłem w jednym z moich filmów:

Pomocne komunikaty dla NullPointerException (Helpful NullPointerExceptions)

Funkcjonalność ta pomaga wyświetlić bardziej szczegółowe komunikaty błędu w przypadku wyjątku NullPointerException.

Żeby włączyć rozszerzone komunikaty, musimy ustawić JVM odpowiednią flagę:
-XX:+ShowCodeDetailsInExceptionMessages

Wtedy np. dla takiego wywołania a.b.c.d = 100; gdy a.b jest nullem dostaniemy:

Exception in thread "main" java.lang.NullPointerException:
    Cannot read field "c" because "a.b" is null
    at App.main(App.java:5)

Pattern Matching for instanceof

Kolejna zmiana w tej wersji to zapowiedź Pattern Matchingu dla instanceof, który wszedł ostatecznie do Javy w wersji 16. Funkcjonalność ta skraca trochę zapis sprawdzania typów poprzez instanceof. Do tej pory wyglądało to mniej więcej tak:

if (obj instanceof String) {
  String string = (String) obj; 
  System.out.println(string);
}

Wewnątrz bloku if, musieliśmy deklarować zmienną dla sprawdzanego obiektu, żeby z rzutować go do sprawdzanego typu. Trochę było to bez sensu, ponieważ już sprawdziliśmy, że to jest właśnie ten typ, więc rzutowanie tutaj wydaje się bardzo nadmiarowe.

Z Pattern Matchingiem jest to trochę prostsze:

if (obj instanceof String string) {
  System.out.println(string); 
} else { 
  // nie możesz użyć tutaj zmiennej string 
}

Java 15

Zapieczętowane klasy (Sealed Classes)

W tej wersji Javy swoją pierwszą zapowiedź miały zapieczętowane klasy (Sealed Classes), które ostatecznie weszły do języka w wersji 17. Zapieczętowane klasy to klasy, które ograniczają możliwość dziedziczenia po sobie do tylko wylistowanych klas. Żeby odpowiednio wylistować klasy, które mogą dziedziczyć po danej klasie, musimy użyć słowa kluczowego permits.

public abstract sealed class Animal permits Cat, Dog, Elephant {...}

I po takiej klasie mogą dziedziczyć tylko wymienione trzy klasy: Cat, Dog, Elephant.

Java 16

W tej wersji właściwie nie było, żadnych nowych funkcjonalności. Na stałe weszły do Javy wcześniej zapowiadane funkcjonalności takie jak: Rekordy i Pattern Matching dla Instanceof. Kolejną zapowiedź miały klasy zapieczętowane.

Ciekawa zmiana miała miejsce w strumieniach. Dodano metodę .toList(), dzięki czemu przy użyciu strumieni nie trzeba już robić .collect(Collectors.toList()), tylko wystarczy .toList()

Java 17

W tej wersji do języka weszły zapieczętowane klasy, które opisałem już wcześniej w tym artykule, a wszystkie istotne zmiany w Javie 17 opisałem w poprzednim artykule: Java 17 LTS.

Pattern Matching dla instrukcji switch

Z nowych rzeczy pojawiła się pierwsza zapowiedź Pattern Matching dla instrukcji switch, co prawda jest preview, ale jeśli bardzo chcemy używać Pattern Matchingu w switchach to możemy sobie go włączyć w Javie 17 poprzez uruchomienie Javy z parametrem --enable-preview. I w tej wersji Pattern Matching pozwala na używanie wyrażeń warunkowych w instrukcji switch. Wygląda to mniej więcej tak:

switch (s) { 
  case Triangle t && (t.calculateArea() > 100) -> System.out.println("Large triangle"); 
  case Triangle t -> System.out.println("Small triangle"); 
  default -> System.out.println("Non-triangle"); 
}

Możemy także używać różnych typów jako wartości w case’ach:

String formatted = switch (o) {
    case Integer i -> String.format("int %d", i);
    case Long l    -> String.format("long %d", l);
    case Double d  -> String.format("double %f", d);
    case String s  -> String.format("String %s", s);
    default        -> o.toString();
};

A także nulla:

switch (s) {
  case null         -> System.out.println("Oops");
  case "Foo", "Bar" -> System.out.println("Great");
  default           -> System.out.println("Ok");
}

Podsumowanie

Całkiem sporo nowych wersji Javy ukazało się od Ostatniego LTSa, bo było ich aż 6, ale tak naprawdę zmian w języku jest nie wiele. Najistotniejsze to moim zdaniem rekordy. Rekordy w Javie pozwolą ograniczyć trochę uciążliwe generowanie getterów, konstruktorów itd., więc jest to duża zmiana na plus.

Chociaż w tej kwestii, od kilku już lat można używać Lomboka, i w ten sposób skracać sobie generowanie dodatkowych metod. Ale trzeba pamiętać, że Lombok nie jest natywną konstrukcją języka, a procesorem adnotacji, przez co może sprawiać pewnego rodzaju problemy. I tak jak ktoś kiedy zauważył pod jednym z moim artykułem, Lombok do działania wykorzystuje pewien hak związany z kompilatorem, co może być w przyszłości problematyczne.

Kolejna z mniej istotnych, ale przydatnych funkcjonalności to bloki tekstu, dzięki którym będzie można trochę łatwiej obchodzić się z wielolinijkowymi stringami.

Natomiast dla developerów, którzy trochę programowali w językach funkcyjnych, bardziej przydatne będzie pewnie wprowadzenie Pattern Matchingu w instrukcjach switch. Do tej pory instrukcje switch były raczej sporadycznie używane w Javie, być może po tych zmianach, to się trochę zmieni.

Źródło:

Zmiany w Javie od wersji 8 do Java 11

https://openjdk.java.net/projects/jdk/

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat z czego ponad 8 programuję w Javie. Zapraszam Cię do lektury mojego bloga. Możesz przeczytać więcej o mnie >>TUTAJ<<

3 thoughts to “Zmiany w Javie od wersji 11 do Java 17”

  1. Cześć!

    Dzięki za przedstawienie historii co działo się w Javie od 11 wersji. Najgorsze jest to, że większość projektów cały czas chodzi na „wygrzanej” 8 i nie można korzystać w nich z nowych elementów API. Obecnie jedyne co mogę zrobić to w swoich „Pet Projects” korzystać z dobrodziejstw nowych wersji.

    Pozdrawiam!

    1. Dzięki Cezary. Rozumiem jak to jest. Też kiedyś miałem takie problemy. Jedyne co można z tym zrobić to migrować. A jak nie zależy to od Ciebie to próbować przekonywać tych, od których to zależy. W większości przypadków da się to zrobić bez problemów, jedyny problem, to najczęściej to, że się komuś nie chce. A korzyści jest całkiem sporo, nowe funkcjonalności języku to tylko część. Są jeszcze poprawki wydajnościowe, nowe funkcje w JVM, wiele naprawionych bugów w JVM itd.

  2. Dobre zestawienie, przystępnie wytłumaczone.
    Trochę szkoda że piszesz o zapowiedziach zmian, a nie tylko co faktycznie weszło.
    Trochę utrudnia to połapać się co kiedy faktycznie nowego weszło.

Komentarze są zamknięte.