Introduction to the

Java 8 introduces lambda expressions, which essentially represent an anonymous function.

Prior to Java 8, using anonymous functions required a new class implementation, but with lambda expressions, everything became very brief.

Let’s look at an example from the thread pool earlier:

//ExecutorService using class
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(new Runnable() {
            @Override
            public void run(a) {
            log.info("new runnable"); }});Copy the code

Executorservice.submit takes a Runnable class, and in the example above we created a new Runnable class and implemented its run () method.

The above example, if rewritten as a lambda expression, would look like this:

//ExecutorService using lambda
        executorService.submit(()->log.info("new runnable"));
Copy the code

As simple as it seems, lambda expressions can be used to omit anonymous class constructs and make them more readable.

So can all anonymous classes be refactored using lambda expressions? Also is not.

Let’s look at the features of the Runnable class:

@FunctionalInterface
public interface Runnable 
Copy the code

The Runnable class has an @functionalInterface annotation on it. This annotation is the Functional Interface that we will talk about today.

Functional Interface

FunctionalInterface is an Interface annotated with @functionalInterface. It is characterized by an abstract method that only one subclass must implement. If the abstract method is preceded by the default keyword, it is not evaluated.

This also makes sense because Functional Interface, when rewritten as a lambda expression, does not specify which method to implement, which can be problematic if you have more than one method to implement.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
Copy the code

Functional Interface is usually in the java.util.function package.

Functional Interface can be divided into several types, depending on the parameters and return values of the methods to be implemented.

Function: One return value for one argument

The Function interface defines a method that takes one argument and returns one.

@FunctionalInterface
public interface Function<T.R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);Copy the code

Function is usually used when working with collection classes.

Map<String, Integer> nameMap = new HashMap<>();
        Integer value = nameMap.computeIfAbsent("name", s -> s.length());
Copy the code

In the example above we call the map’s computeIfAbsent method, passing in a Function.

The above example could be made even shorter:

Integer value1 = nameMap.computeIfAbsent("name", String::length);
Copy the code

Function does not specify the type of argument and return value. If you need to pass in specific arguments, you can use IntFunction, LongFunction, DoubleFunction:

@FunctionalInterface
public interface IntFunction<R> {

    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    R apply(int value);
}
Copy the code

ToIntFunction, ToLongFunction, ToDoubleFunction can be used if you need to return specific parameters:

@FunctionalInterface
public interface ToDoubleFunction<T> {

    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    double applyAsDouble(T value);
}
Copy the code

If you want to specify both parameters and return values, You can use DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction, IntToLongFunction, LongToIntFunction, LongToDoubleFunction:

@FunctionalInterface
public interface LongToIntFunction {

    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    int applyAsInt(long value);
}
Copy the code

BiFunction: Receives two arguments, one return value

BiFunction: BiFunction, ToDoubleBiFunction, ToIntBiFunction, ToLongBiFunction, etc.

@FunctionalInterface
public interface BiFunction<T.U.R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);
Copy the code

Let’s look at an example of BiFunction:

//BiFunction
        Map<String, Integer> salaries = new HashMap<>();
        salaries.put("alice".100);
        salaries.put("jack".200);
        salaries.put("mark".300);

        salaries.replaceAll((name, oldValue) ->
                name.equals("alice")? oldValue : oldValue +200);
Copy the code

Supplier: Function without parameters

If none of the parameters are needed, Supplier can be used:

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get(a);
}
Copy the code

Consumer: Accepts an argument without returning a value

Consumer takes an argument but does not return any value. Let’s look at the definition of Consumer:

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
Copy the code

Take a look at a Consumer application:

//Consumer
        nameMap.forEach((name, age) -> System.out.println(name + " is " + age + " years old"));
Copy the code

Predicate: Accepts a parameter and returns a Boolean

Predicate takes a single argument and returns a Boolean:

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
Copy the code

This is great if used for filtering collection classes:

//Predicate
        List<String> names = Arrays.asList("A"."B"."C"."D"."E");
        List<String> namesWithA = names.stream()
                .filter(name -> name.startsWith("A"))
                .collect(Collectors.toList());
Copy the code

Operator: Receives and returns the same type

Operator receives and returns the same type. There are many types of operators: UnaryOperator BinaryOperator, DoubleUnaryOperator, IntUnaryOperator, LongUnaryOperator, DoubleBinaryOperator, IntBinaryOperator, LongBinaryOperator, etc.

@FunctionalInterface
public interface IntUnaryOperator {

    /**
     * Applies this operator to the given operand.
     *
     * @param operand the operand
     * @return the operator result
     */
    int applyAsInt(int operand);
Copy the code

Let’s look at an example of a BinaryOperator:

 //Operator
        List<Integer> values = Arrays.asList(1.2.3.4.5);
        int sum = values.stream()
                .reduce(0, (i1, i2) -> i1 + i2);
Copy the code

conclusion

Functional Interface is a useful new feature that I want you to get your head around.

Examples of this article: github.com/ddean2009/l…

For more, visit www.flydean.com