Hystrix is Netflix’s open source stream limiting and fusion-downscaling component. Last year, I found that Hystrix was no longer updated. On github, I was directed to another alternative project, Resilience4J, which is based on Java 8 and uses only the VAVR library. And that’s who we’re going to introduce today.

Lambda expressions

Vavr is designed to enhance the functional programming experience in Java, so here is an introduction to functional programming in Java.

Java 8 introduced the functional programming paradigm by passing functions as arguments to other functions. Prior to Java 8, Java supported similar functionality, but with polymorphisms using interfaces or anonymous classes. There is a lot of template code in both interfaces and anonymous classes, so Java 8 introduces Lambda expressions to formally support functional programming.

For example, if we wanted to implement a comparator to compare the size of two objects, prior to Java 8, we could only use the following code:

Compartor<Apple> byWeight = new Comparator<Apple>() {
  public int compare(Apple a1, Apple a2) {
    returna1.getWeight().compareTo(a2.getWeight()); }}Copy the code

The above code can be written using Lambda expressions as follows (IDEA prompts you for code simplification) :

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Copy the code

Here are some things you should know about Lambda expressions:

  • Lambda expressions can be thought of as anonymous functions: they have no name, but have a list of arguments, a function body, a return type, and perhaps a list of exceptions that can be thrown.
  • A functional interface is one that declares only an abstract method;
  • The @functionalInterface annotation works for functional interfaces in the same way that @Override works for methods being overridden — it’s not required, but it helps readability, so if you’re defining your own functional interfaces in development, it’s a good idea to use this annotation as well;
  • Java 8 comes with some commonly used functional interfaces in thejava.util.functionPackages, including Predicate, Function<T,R>, Supplier, Consumer, BinaryOperator, and so on.

vavr

The Java standard library’s API support for functional programming is relatively limited due to its versatility requirements and binary file size. Function declarations are only available as Function and BiFunction, and the number of operations supported on streams is small. For these reasons, you may need VAVR to help you better use Java 8 for functional development.

Vavr is an attempt to make Java have a similar syntax to Scala. Vavr provides an immutable set framework; Better functional programming features; Tuples.

A collection of

Vavr implements a new Java collections framework to match the functional programming paradigm, and Vavr provides immutable collections. Using Stream in Java requires showing the steps to convert the collection to Steam, whereas in VAVR such steps are eliminated.

  1. Vavr’s List is an immutable List, and operations on that List object generate a new List object. Using Java 8 code:
Arrays.asList(1.2.3).stream().reduce((i, j) -> i + j);

IntStream.of(1.2.3).sum();
Copy the code

Using VAVR to do the same thing is more straightforward:

//io.vavr.collection.List
List.of(1.2.3).sum();
Copy the code
  1. Vavr’s Stream is an inert linked list, and elements are evaluated only when necessary, so most operations can be done in constant time.

Functions

Java 8 provides a Function interface that accepts one argument and a BiFunction that accepts two arguments, while VAVR provides a Function interface that accepts up to eight arguments: Function0, Function1, Function2, Function3, Function4…… Function8.

Vavr also offers more functional programming features:

  • In mathematics, a combination of functions can take two functions to form a third function. For example, the function F :X->Y and the function g:Y->Z can be combined to form h:g(f(X)), representing X->Z. Here’s an example of a combination:
public class VavrFunctionExample {

    @Test
    public void testCompose(a) {
        / / use andThen
        Function1<Integer, Integer> plusOne = a -> a + 1;
        Function1<Integer, Integer> multiplyByTwo = a -> a * 2;
        Function1<Integer, Integer> add1AndMultiplyBy2 = plusOne.andThen(multiplyByTwo);
        Assert.assertEquals(6, add1AndMultiplyBy2.apply(2).intValue());

        / / use compose
        Function1<Integer, Integer> add1AndMultiplyBy2WithCompose = multiplyByTwo.compose(plusOne);
        Assert.assertEquals(6, add1AndMultiplyBy2WithCompose.apply(2).intValue()); }}Copy the code
  • Lifting Do you often write code to call a function and determine whether its return value meets requirements, or to catch all exceptions in case of exceptions, or even catch(Throwable t). The Lifting feature is designed to solve this problem by internally handling exceptions and converting them to a special result, None, so that the results of the function can be handled in a uniform mode outside the function. Here’s an example:
public class VavrFunctionExample {
    @Test
    public void testLifting(a) {
        Function2<Integer, Integer, Integer> divide = (a, b) -> a / b;
        Function2<Integer, Integer, Option<Integer>> safeDivide = Function2.lift(divide);

        // = None
        Option<Integer> i1 = safeDivide.apply(1.0);
        Assert.assertEquals("None", i1.toString());

        // = Some(2)
        Option<Integer> i2 = safeDivide.apply(4.2);
        Assert.assertEquals(2, i2.get().intValue()); }}Copy the code
  • Curring refers to the process of turning a function that originally took multiple arguments into a new function that took one argument. For Java, it’s easy to provide default methods. Here’s an example:
public class VavrFunctionExample {
    @Test
    public void testCurried(a) {
        Function2<Integer, Integer, Integer> sum = (a, b) -> a + b;
        Function1<Integer, Integer> add2 = sum.curried().apply(2);

        Assert.assertEquals(6, add2.apply(4).intValue()); }}Copy the code
  • Memorization is a type of caching in which a method is executed only once and the results of the first execution are always returned. But it should not be used much in practical applications.
public class VavrFunctionExample {
    @Test
    public void testMemorize() {
        Function0<Double> hashCache =
            Function0.of(Math::random).memoized();

        double randomValue1 = hashCache.apply();
        double randomValue2 = hashCache.apply(); Assert.assertTrue(randomValue1 == randomValue1); }}Copy the code

Pattern matching

Pattern matching is a concept in functional programming languages. It is not currently supported in Java. Using VAVR, you can write pattern matching code in Java. The switch in Java… Case statements can only work on constants, whereas pattern matching can work on the return result of another function, which is very powerful. The following examples show the use of if, switch… Case and pattern matching are three examples of syntax that do the same thing. Pattern matching helps reduce the number of lines of code.

import org.junit.Test;

import static io.vavr.API.$;
import static io.vavr.API.Case;
import static io.vavr.API.Match;
import static org.junit.Assert.assertEquals;

public class VavrPatternExample {

    @Test
    public void whenIfWorksAsMatcher_thenCorrect(a) {
        int input = 3;
        String output;
        if (input == 0) {
            output = "zero";
        }
        if (input == 1) {
            output = "one";
        }
        if (input == 2) {
            output = "two";
        }
        if (input == 3) {
            output = "three";
        } else {
            output = "unknown";
        }

        assertEquals("three", output);
    }

    @Test
    public void whenSwitchWorksAsMatcher_thenCorrect(a) {
        int input = 2;
        String output;
        switch (input) {
            case 0:
                output = "zero";
                break;
            case 1:
                output = "one";
                break;
            case 2:
                output = "two";
                break;
            case 3:
                output = "three";
                break;
            default:
                output = "unknown";
                break;
        }

        assertEquals("two", output);
    }

    @Test
    public void whenMatchworks_thenCorrect(a) {
        int input = 2;
        String output = Match(input).of(
            Case($(1), "one"),
            Case($(2), "two"),
            Case($(3), "three"),
            Case($(), "?"));

        assertEquals("two", output); }}Copy the code

The resources

  1. Java 8 In Action
  2. Github.com/resilience4…
  3. www.baeldung.com/vavr
  4. www.vavr.io/vavr-docs/

This issue focuses on topics such as backend technology, JVM troubleshooting and optimization, Java interview questions, personal growth and self-management, providing readers with front-line developer work and growth experience, looking forward to your harvest here.