Java 8 na wypasie – biblioteka Vavr


Java 8 na wypasie – biblioteka Vavr

Artykuł ten bazuje na wpisie – https://www.baeldung.com/vavr – gorąco zachęcam do zapoznania się również z nim.

Biblioteka vavr rozszerza możliwości programowania funkcyjnego w Java 8:

<dependencies>
     <dependency>
         <groupId>io.vavr</groupId>
         <artifactId>vavr</artifactId>
         <version>0.9.0</version>
     </dependency>
</dependencies>

Wartość Null

Poniżej zamieszczony kod może powodować błąd w przypadku braku weryfikacji wartości null:

public static void nullWithoutVavr() {
    Object object = null;
    if (object == null) {
        object = "obj";
    }
}

biblioteka vavr ten problem eliminuje zamieniając wartość null na wartość None:

public static void nullWithVavr() {
    Option<Object> obj = Option.of(null);
    System.out.println(obj);
}

wynik:

None

Process finished with exit code 0

inny przykład to zwrócenie wartości default w momencie kiedy wystąpi null:

public static void nullWithVavrDefaultValue() {
    Object object = null;
    Option<Object> objectOption = Option.of(object);
    System.out.println( objectOption.getOrElse("javaleader.pl"));
}

wynik:

javaleader.pl

Process finished with exit code 0

Tuple (krotka)

Reprezentacja krotek (tupli – niemodyfikowalna struktura danych) nie wystepuje w Javie. Z użyciem biblioteki Vavr tupla dwuelementowa przedstawia się nastepująco:

public static void tuple() {
    Tuple2<String, String> java8 = Tuple.of("pl", "Javaleader");
    System.out.println(java8._1);
    System.out.printf(java8._2);
}

wynik:

pl
Javaleader
Process finished with exit code 0

Konstrukcja Try

Jeśli metoda wyrzuca wyjątek to należy przy jej wywołaniu opakować ją w strukturę try & catch co zmniejsza czytelność naszego kodu. Biblioteka vavr daje większe możliwości zwiększając czytelność kodu:

public static void tryIsFailue() {
    Try<Integer> result = Try.of(() -> 1 / 0);
    System.out.println(result.isFailure());
}

wynik:

true

Process finished with exit code 0

zwrócenie wartości domyślnej w momencie wystąpienia błędu:

public static void tryWithDefaultValue() {
    Try<Integer> result = Try.of(() -> 1 / 0);
    System.out.println(result.getOrElse(0));
}

wynik:

0

Process finished with exit code 0

Interfejsy funkcyjne

Biblioteka vavr udostępnia więcej interfejsów funkcyjnych niż te dostępne w Java 8:

Interfejs Function z 5 parametrami:

public static void functionInterfaceWith5Arguments() {
    Function5<Integer, Integer, Integer, Integer, Integer, Integer> integersSum = (a, b, c, d, e) -> a + b + c + d + e;
    Integer sum = integersSum.apply(1,2,3,4,5);
    System.out.println(sum);
}

wynik:

15

Process finished with exit code 0

Możliwe jest także użycie referencji do metody:

private static int sumOf5Integers(int a, int b, int c, int d, int e) {
    return a + b + c + d + e;
}

public static void functionInterfaceWith5Arguments() {
    Function5<Integer, Integer, Integer, Integer, Integer, Integer> integersSum = FunctionalProgramming::sumOf5Integers;
    Integer sum = integersSum.apply(1,2,3,4,5);
    System.out.println(sum);
}

Kolekcje

Kolekcje w Javie to struktury modyfikowalne. Struktury modyfikowalne mają to do siebie, że nie są bezpieczne w wielowątkowych aplikacjach. Są oczywiście rozwiązania dla kolekcji które mają być bezpieczne wątkowo ale nie są to wydajne mechanizmy. Ponadto kolekcja która tworzona jest jako niemodyfikowalna w momencie próby dodania nowego elementu zwraca błąd:

public static void immutableCollectionThrowsError() {
    java.util.List<String> javaleader = Arrays.asList("javaleader");
    java.util.List<String> list       = Collections.unmodifiableList(javaleader);
    list.add(".pl");
}
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055)
	at pl.javaleader.FunctionalProgramming.immutableCollectionThrowsError(FunctionalProgramming.java:62)
	at pl.javaleader.FunctionalProgramming.main(FunctionalProgramming.java:79)

Z pomocą przychodzi nowe API dla kolekcji z użyciem biblioteki vavr!

Listy z użyciem Api biblioteki vavr nie posiadają metody add(). Zamiast tego posiadają metody append() i prepend() które odpowiednio dodają element na końcu listy i na początku tworząc nową listę co z kolei skutkuje że stara lista pozostaje niemodyfikowalna!

public static void imutableCollectionWithVavr() {
    List<Integer> intList1 = List.of(1, 2, 3);
    List<Integer> intList2 =  intList1.append(4);
    System.out.println(intList1 == intList2); // false
}

Walidatory

Walidatory:

import io.vavr.control.Validation;

dają możliwość akumulacji wszystkich błędów dla walidowanych obiektów. Zakładamy dla użytkownika następujące reguły walidacji:

  • email musi być prawidowy,
  • wiek musi być powyżej >18.

Klasa użytkownik:

@Getter
@Setter
@AllArgsConstructor
class User {
    private String email;
    private int age;
}

piszemy walidator:

class UserValidator {

    String EMAIL_ERR = "Invalid email";
    String AGE_ERR   = "Age must be grater than 18";

    public Validation<Seq<String>, User> validateUser(String email, int age) {
        return Validation.combine(validateEmail(email), validateAge(age)).ap(User::new);
    }

    private Validation<String, String> validateEmail(String name) {
        String invalidChars = name.replaceAll("^(.+)@(.+)$", "");
        return invalidChars.isEmpty() ? Validation.valid(name) : Validation.invalid(EMAIL_ERR + invalidChars);
    }

    private Validation<String, Integer> validateAge(int age) {
        return age < 18 ? Validation.invalid(AGE_ERR) : Validation.valid(age);
    }
}

metoda która waliduje użytkownika:

public static void testUserValidator() {
    UserValidator userValidator           = new UserValidator();
    Validation<Seq<String>, User> valid   = userValidator.validateUser("kontakt@javaleader.pl", 30);
    Validation<Seq<String>, User> invalid = userValidator.validateUser("kontaktjavaleader.pl", 14);
    System.out.println(valid.toString());
    System.out.println(invalid.toString());

    invalid.getError().forEach(error -> System.out.println(error));
}

wynik:

Valid(pl.javaleader.User@7106e68e)
Invalid(List(Invalid email kontaktjavaleader.pl, Age must be grater than 18))
Invalid email kontaktjavaleader.pl
Age must be grater than 18

Pattern Matching

Instrukcje if lub wyrażenia case nie są dobrym mechanizmem sprawdzania wartości parametru wejściowego jeśli występuje więcej warunków:

instrukcja if():

int input     = 1;
String output = "";

if (input == 0) {
    output = "zero";
}
if (input == 1) {
    output = "one";
}

instrukcja case():

switch (input) {
    case 0:
        output = "zero";
        break;
    case 1:
        output = "one";
        break;
}

zamiast tego lepiej użyć konstrukcji:

public static void patternMatching() {
    int input = 3;
    String output = Match(input).of(
            Case($(1), "one"),
            Case($(2), "two"),
            Case($(3), "three"),
            Case($(), "?"));
    System.out.println(output);
}

wynik:

three

Process finished with exit code 0

inny przykład:

public static void showHelp() {
    System.out.println("help...");
}
public static void patternMatching() {
    String arg = "-h";
    Match(arg).of(
            Case($(isIn("-h", "--help")), o -> run(FunctionalProgramming::showHelp)),
            Case($(), o -> run(() -> {
                throw new IllegalArgumentException();
            }))
    );

}

wynik:

help...

Obiekty Lazy

Obiekty typu Lazy są ewaluowane tylko raz w momencie pierwszego odwołania. W kolejnych wywołaniach zwracany jest ten sam wynik zapisany w cache’u. Zwiększa to wydajność aplikacji ponieważ nie trzeba potwrzać za każdym razem wykonywania kodu który jest niezbędny do wygenerowania wyniku:

public static void lazy() {

    Lazy<Double> lazy = Lazy.of(Math::random);
    System.out.println(lazy.isEvaluated());

    double val1 = lazy.get();
    System.out.println(lazy.isEvaluated());
    System.out.println(val1);

    double val2 = lazy.get();
    System.out.println(lazy.isEvaluated());
    System.out.println(val2);
}

wynik:

false
true
0.24536330044331622
true
0.24536330044331622


Leave a comment

Your email address will not be published.


*