Classification and use of functional interfaces in Java

brief introduction

Java 8 introduces lambda expression, which actually represents an anonymous function.

Before Java 8, if you need to use anonymous function, you need to implement a new class, but with lambda expressions, everything becomes very simple.

Let's take a look at an example when we talked about thread pool earlier:

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

executorService. Submit needs to receive a runnable class. In the above example, we new a runnable class and implement its run () method.

If the above example is rewritten with lambda expression, it is as follows:

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

It seems that it is not very simple. You can omit the construction of anonymous classes by using lambda expressions, and it is more readable.

So can all anonymous classes be refactored with lambda expressions? Neither.

Let's take a look at the characteristics of runnable class:

@FunctionalInterface
public interface Runnable 

The runnable class has an @ functionalinterface annotation on it. This annotation is the functional interface we want to talk about today.

Functional Interface

Functional interface refers to the interface with @ functional interface annotation. It is characterized by the abstract method in which only one subclass must be implemented. If the abstract method is preceded by the default keyword, no calculation will be performed.

In fact, this is also easy to understand, because after the functional interface is rewritten into a lambda expression, it does not specify which method to implement. If there are multiple methods to implement, there will be problems.

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

Functional interfaces are generally in Java util. Function package.

Functional interfaces can be divided into many types according to the method parameters and return values to be implemented. We will introduce them respectively below.

Function: one parameter and one return value

The function interface defines a method that receives a parameter and returns a parameter.

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

Generally, we will use function when processing collection classes.

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

In the above example, we called the computeifabsent method of map and passed in a function.

The above example can also be rewritten into a shorter one:

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

Function does not specify the type of parameter and return value. If you need to pass in specific parameters, you can use intfunction, longfunction and 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);
}

If you need to return specific parameters, you can use tointfunction, tolongfunction, todoublefunction:

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

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

Bifunction: receives two parameters, one return value

If you need to accept two parameters and one return value, you can use 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);

Let's take 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);

Supplier: function without parameters

If you don't need any parameters, you can use supplier:

@FunctionalInterface
public interface supplier<T> {

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

Consumer: receives a parameter and does not return a value

Consumer receives a parameter but does not return any value. Let's see 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);

See the specific application of a consumer:

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

Predict: receive a parameter and return Boolean

Predict receives a parameter and returns a Boolean value:

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

It is excellent if it is used for collection class filtering:

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

Operator: receive and return the same type

Operators receive and return the same types. 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);

Let's take an example of binaryoperator:

 //Operator
        List<Integer> values = Arrays.asList(1,2,3,4,5);
        int sum = values.stream()
                .reduce(0,(i1,i2) -> i1 + i2);
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>