Kategoria: java

Java JDK 15 co nowego?

 Nowe wydanie Javy JDK 15 z 15 września 2020 zawiera wiele nowych ciekawych i poprawionych funkcji. 

JEP 339: Edwards-Curve Digital Signature Algorithm (EdDSA)

JEP 360: Sealed Classes (Preview) 
JEP 371: Hidden Classes 
JEP 372: Remove the Nashorn JavaScript Engine 
JEP 374: Disable and Deprecate Biased Locking 
JEP 375: Pattern Matching for instanceof (Second Preview) 
JEP 377: ZGC: A Scalable Low-Latency Garbage Collector 
JEP 378: Text Blocks 
JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector J
EP 381: Remove the Solaris and SPARC Ports 
JEP 383: Foreign-Memory Access API (Second Incubator)
JEP 384: Records (Second Preview) 
JEP 385: Deprecate RMI Activation for Removal

Wyjątki -informacje warte zapamiętania

W poście tym zebrałem informacje, które warto zapamiętać na temat wyjątków w Javie. Wyjątek wskazuje na to, że coś nieoczekiwanego zostało wykonane w programie. Metoda tego typu służy do zaprogramowania to w jaki sposób aplikacja ma na tego typu zachowanie reagować.

Wyjątki w naszym kodzie tworzymy za pomocą throw new exception()

Typy wyjątków

Errors

Rozszerzają klasę  java.lang.Error . Są wywoływane przez JVM. Programista nie powinien zajmować się obsługą błędów tego typu. Błędy z klasy Error są rzadkie, przykłady:
1. ExceptionInInitializerError
2. StackOverflowError
3. NoClassDefFoundError

UNCHECKED exception czyli  java.lang.RuntimeException

Subklasy dziedziczące po klasie java.lang.RuntimeException są wyjątkami typu UNCHECKED. Jest wywoływana przez programistę lub JVM. Jeżeli są wywoływane przez JVM to programista nie powinien zajmować się ich dodatkowa obsługą. W momencie gdy wystąpią w naszej aplikacji łatwiej będzie znaleźć kontekst ich wystąpiania. Popularne wyjątki:
    a. ArithmeticException
    b. ArrayIndexOutOfBoundsException
    c. ClassCastException
    d. IllegalArgumentException
    e. NullPointerException
    f. NumberFormatException
IllegalArgumentException oraz NumberFormatException są wyjątkami typowo wywoływanymi przez programistę, pozostałe są wywoływane przez JVM.

CHECKED exception czyli  java.lang.Exception 

Tego typu wyjątki zawierają wszystkie wyjątki typu java.lang.Exception
oprócz wyjątków typu RuntimeExceptionjava.lang.Exception  są wyjątkami typu CHECKED. Java wymaga aby wyjątki typu CHECKED były obsłużone w naszej aplikacji. Mogą zostać wywołane przez JVM lub programistę. Popularne wyjątki:
1. FileNotFoundException
2. IOException
Inne zasady warte zapamiętania:
1. W przypadku gdy wyrażenie try posiada wiele bloków typu catch może zostać wywołany jedynie ten jeden, który jest zadeklarowany wcześniej.W przypadku gdy występuje blok try i finally to finally ma większy priorytet i to on zostaje wywołany.
2. Kiedy metoda nadpisuje metodę w superklasie lub interfejsie nie można dodawać wyjątków typu Checked exception (java.lang.Exception). Nie można też w klasie dziedziczącej deklarować mniejszą ilość wyjątków
3. Wyjątki w metodach deklarujemy za pomocą słowa: throws.

Dwu argumentowy predykat, funkcja, konsument. Opis i porównanie

W poście tym przybliżam ciekawą funkcję wprowadzoną w Java8 jaką jest umożliwienie wprowadzania dwóch argumentów do Predykatu, Funkcji, Konsumenta i Dostawcy.

1. Predykat (Predicate)

Jest to funkcja, która przyjmuje jakiś argument i odpowiada: „tak” lub „nie”, w zależności od podanego jej elementu. Przykład predykatu jedno argumentowego gdzie sprawdzamy czy zmienna jest parzysta lub nie:
Predicate<Integer> p = i-> i%2==0;
W przypadku gdy predykat zwróci „tak”, to metoda umieści ten element w kolekcji wynikowej. W przeciwnym razie, ten element się tam nie znajdzie.

BiPredicate daje nam możliwość sprawdzenia warunków dla 2 argumentów. Poniżej przykład użycia:

import java.util.function.*
class Test {
public static void main(String [] args){
        BiPredicate<Integer, Integer> p = (a,b)  ->  (a+b) % 2  == 0;
        log.debug(p.test(10,20); // true
        log.debug(p.test(15); // false
    }
}

2. Funkcja

Przykład użycia funkcji gdzie na wejściu podajemy jeden argument typu Integer. W wyniku wykonania funkcji zwracamy mnożenie arguemntu prez samego siebie. Czyli jest to
function <Integer, Integer> f = i -> i*i

BiFunkcja w odróżnieniu od funkcji przedstawionej powyżej może na wejściu przyjąć 2 wartości i je przetworzyć.  Poniżej przykład użycia:

import java.util.function.*
class Test {
public static void main(String [] args){
        BiFunction<Integer, Integer, Integer> f = (a,b) -> a*b
        log.debug(p.apply(10,20); // 200
        log.debug(p.apply(1, 10); // 10
    }
}

3 Konsument

Przykład użycia konsumenta gdzie na wejściu przyjmujemy argument typu String. Następnie przekazujmy go do funkcji sopen.
Consumer<String> c = a -> sopen(a)

BiKonsument może przetworzyć na wejściu 2 argumenty.

Przykład użycia BiFunction oraz BiConsumer:
import java.util.function.*;
import java.util.*
class Employee {
    String name;
    double salary;

Employee(String name, double salary){
    this.name = name;
    this.salary = salary;
}
class Test {
    public static void main(String[] args){
        ArrayList<Employee> list = new ArrayList<Employee>();
        BiFunction<String, double, Employee> f = (name,salary) -> new Employee(name, salary);

    list.add(f.accept(„Adam”, 1000000000));
    list.add(f.accept(„Ada”, 1000));
    list.add(f.accept(„Anna”, 1000));

BiConsumer<Employee, Double> c = (e,d) -> e.salary = e.salary + d;

for (Employee e: list){
    c.accept(e, 600);

}

    for (Employee e :l){
        log.debug(e.salary);
        log.debug(e.name);
    }
    }
}

4. Dostawca (Supplier)

Zwraca (dostarcza) wartość typu Date.

Supplier<Date> s = () -> new Date();

Porównanie jedno i dwu argumentowego predykatu i funkcji:

Jedno argumentowy interfejs funkcjonalny. 
interface Predicate<T> {
    public boolean test(T t);
    default Predicate and(Predicate p);
    default Predicate or(Predicate p);
    default Predicate negate();
    static Predicate isEqual(Object o);
}

Zawiera jedna metodę abstrakcyjna (test) oraz 4 domyśle metody i jedna metodę statyczna.


Dwu argumentowy interfejs funkcjonalny:

interface BiPredicate<T, U> {
    public boolean test(T t, U u);
    default BiPredicate and(BiPredicate p);
    default BiPredicate or(BiPredicate p);
    default BiPredicate negate();
}
Nie posiada metody statycznej. Przyjmuje 2 argumenty w metodzie abstrakcyjnej.
Interfejs Funkcji:
interface Function<T,R> {
    public R apply(T t);
    default Function andThen(Function f);
    default Function compose(Function f);
          static Function identity();
}
Posiada jedne argument na wejściu (T) oraz zwraca jeden argument (R). Jedna metode abstrakcyjna oraz 2 domyślne i jedna statyczna.
Interfejs BiFunkcji:
interface BiFunction<T, U, R> {
    public R apply(T t, U u);
    default BiFunction andThen(Function f);
}
Posiada 2 argumenty na wejściu (T,U) oraz zwraca jeden argument (R). Warto zauważyć że domyślna metoda andThen przyjmuje na wejściu Funkcję f a nie BiFunckję.
Jednoargumentowy interfejs Konsument:
interface Consumer<T> {
    public void accept(T t);
    default Consumer andThen(Consumer c);
}
Przyjmuje jeden argument i nic nie zwraca. 
Dwuargumentowy interfejs Konsument:
interface Consumer<T> {
    public void accept(T t);
    default BiConsumer andThen(Consumer c);
}
Przyjmuje dwa argumenty ale nic nie zwraca. Metoda andThen używamy do łaczenia większej ilości Konsumentów.
    

Reactive streams i projekt Reactor ogólne informacje

W tym spisie zebrałem krótkie informacje na temat Reactive streams i project Reactor. Są również bardzo ciekawe linki, które w szybki sposób pomogą Ci wskoczyć na poziom umożliwiający programowanie z użyciem wspomnianych bibliotek.

Elementy występujące w Reactive streams możemy podzielić na:

  1. Publisher
  2. Subscriber
  3. Subscription
  4. Processor

Kiedy Subscriber zaczyna subskrybować Publisher zostaje utworzona Subskrybcja. Subskrybcja jest następnie przesyłana do metody onSubscribe.

Subscriber wysyła rządanie o elementy za pomocą onSubscribe i Publisher wysyła je. W przypadku gdy Publisher nie ma już więcej elementów do wysłania wywołana zostaje metoda onComplete.

W momencie gdy wystąpi błąd, subsrkypcja zostaje przerwana i zostaje wywołana metoda onError.
src: https://github.com/reactive-streams/reactive-streams-jvm


Projekt reactor

Aby nie tracić czasu podaje poniżej przydatne dla mnie informacje, które od razu pokazują jak stosować najbardziej używane elementy projektu Reactor:

  1. Flux
  2. Mono

Są to implementacje, interfejsu Publisher. Powyższe elementy muszą zostać zasubskrybowane. Można na nich operować za pomocą metod np:

  • map
  • flatMap
W źródłach poniżej znajdują się bardzo przydatne informacje na temat praktycznego użycia Reactor. W linku 1 kiedy używać jakiego operatora np. kiedy używać Mono.just a w którym przypadku jest lepszy Optional. Są też podane informacje jak transformować uzyskane dane.
W linku 2 są natomiast podane najlepsze praktyki. Są tam odpowiedzi na pytania np: Jak odebrać i obsłużyć żądanie synchroniczne.
Praktyczne ćwiczenia znajdują się natomiast w Linku 3 . Jest to projekt na GitHubie utworzony przez twórców projektu Reactor. Musimy w nim ukończyć rozpoczęty kod. Jest to wspaniałe ćwiczenie 🙂

src:

  1. https://projectreactor.io/docs/core/release/reference/#which-operator
  2. https://projectreactor.io/docs/core/release/reference/#faq
  3. https://github.com/reactor/lite-rx-api-hands-on



Interfejsy funkcjonalne wprowadzone w Java 8

Interfejs funkcjonalny jest interfejsem posiadającym przynajmniej jedną metodę abstrakcyjną (single abstract method). W omawianym interfejsie możesz zdefiniować wiele metod abstrakcyjnych i statycznych. Przykład Interfejsu funkcjonalnego:
java.util.function jest pakietem, który zawiera predefiniowane interfejsy.

4.1 Predicate 

Funkcja matematyczna, która zwraca wartość prawda fałsz. w zależności czy warunek zostanie spełniony lub nie. Czyli jest to metoda zwracająca wartość boolean.
– Predicate jest interfejsem funkcjonalnym wprowadzonym od Java 1.8
– interfejs zawiera tylko jedną metodę test(), która przyjmuje jedną wartość, wykonuje sprawdzenie na podstawie przesłanego warunku i  zwraca wartość boolean.

package functional.staticMethods;
import java.util.function.Predicate;

przykład implementacji interfejsu funkcjonalnego Predicate  przyjmującego parametr generyczny typu Integer

public class PredicateSampleInt {
  public static void main(String[] args) {
    Predicate<Integer> p = i -> i>10;
    System.out.println(p.test(100));
    System.out.println(p.test(9)); 

  }

inny przykład implementacji Predicate przyjmującego parametr generyczny typu String

public class PredicateSampleString {
  public static void main(String[] args) {
    Predicate<String> p = s -> s>5;
    System.out.println(p.test(„Ala ma kota));
    System.out.println(p.test(„Kot)); 
  }

inny przykład implementacji Predicate przyjmującego parametr generyczny typu Collection

public class PredicateSampleString {
  public static void main(String[] args) {
ArrayList a = new ArrayList<>{};
a.add(„a”);
    Predicate<Collection> p = c-> c.isEmpty();
    System.out.println(p.test(p(1));
    System.out.println(p.test(3)); 
  } 
istnieje również możliwość łączenia predykatów:
-negaca p1.negate()
-koniunkcja p1.and(p2)
-alternatywa p1.or(p2)

Różnice między interfejsem z metodą domyślną a klasą abstrakcyjną

Poniżej przedstawiam listę różnic pomiędzy interfejsem z metodą domyślną a klasą abstrakcyjną w Java 1.8

Interfejs z metodą domyślną:
1. Wewnątrz interfejsu każda zmienna jest zawsze public static final nie można zadeklarować instancji zmiennej.
2. Interfejs nie przechowuje stanu obiektu
3. Wewnątrz interfejsu nie można deklarować konstruktora
4. Wewnątrz interfejsu nie można deklarować instancji oraz bloków statycznych (static blocks)
5. Interfejs funkcjonalny z metodą domyślną może zawierać wyrażenia lambda
6. Wewnątrz interfejsu nie można nadpisywać metod klasy obiektowej (object class methods)

Klasa abstrakcyjna:
1. Wewnątrz klasy abstrakcyjnej można deklarować instancje zmiennych, które są wymagane w klasach dziedziczących.
2. Klasa abstrakcyjna może przechowywać stan obiektu
3. W klasie abstrakcyjnej można deklarować konstruktory
4. W klasie abstrakcyjnej można deklarować instancje obiektu oraz bloki statyczne
5. Klasa abstrakcyjna nie może zawierać wyrażeń lambda
6. Wewnątrz klasy abstrakcyjnej możesz nadpisywać metody klasy obiektowej

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:

  1. Wyrażenia lambda
  2. Interfejs funkcjonalny (functional interface)
  3. Domyślne i statyczne metody w interfejsach
  4. Interfejsy funkcjonalne dla:
    1. Predicate
    2. Function
    3. Consumer
    4. Supplier
  5. Referencje metod i konstruktorów (method reference, constructor reference) tworzone za przy użyciu operatora ” :: „
  6. Stream API
  7. 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; 
} };

Kolejnym krokiem jest przypisania nowo utworzonego predykatu do zmiennej np:

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ę.

Na koniec możemy użyć zmienną isEven w ten sposób:

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) }

Doszliśmy więc do tytułu pierwszego działu. Teraz przedstawię składnię wyrażenia lambda:

-> 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.

I tak w przypadku gdy jest po lewej stronie tylko jeden argument możemy nie pisać nawiasó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; 
}

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.

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));

Na koniec można jeszcze zrezygnować z nazwy metody. Nasza nowo utworzona metoda jest w anonimowa:

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

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:

  1. Konfiguracja 
    1. Filtrowanie (.filter) w przypadku gdy chcemy wybrać interesujące nas pod elementy
    2. Mapowanie (.map) w przypadku gdy przetwarzamy elementy w Streamie (np. dodajemy)
  2. Przetwarzanie

src: