A functional interface is one that defines only one abstract method. For all functional interfaces, we can create a Lambda expression for it.

  • This article is a record, basically no personal conclusions, if there is any mistake, please correct, thank you very much.
  • [Java] JDK provides a functional interface – nuggets (juejin. Cn)

Functional interface

Here is a brief introduction to functional interfaces.

What is a functional interface

A functional interface is one that defines only one abstract method. Since JDK8 starts to provide the default keyword, a functional interface can define no more than one method, even if an interface has many default and static methods, as long as there is only one abstract method, it is a functional interface.

What does a functional interface do

Functional interfaces are useful in that they allow Lambda expressions to be inline as implementations of the functional interface, with the entire expression as an instance of itself. That is, Lambda expressions can operate anywhere a functional interface is a method parameter.

For example, Runnable is a functional interface because void run() is the only abstract method in it, so Lambda expressions can be applied to places that take Runnable as arguments. Since one of the Thread constructors is Thread(Runnable Runnable), we can do this:

new Thread(() -> System.out.println("HELLO WORLD")).start();
Copy the code

Function descriptor

When a Lambda expression is used as an implementation of a functional interface, the signature of the abstract method of the functional interface is the signature of the Lambda expression. This abstract method is called a function descriptor.

Method signature is the concept of method signature in Java, which is used to judge the uniqueness of methods in the same class. It consists of method name, method parameter type and method parameter number. For functional interfaces, the method name attribute is ignored because only one abstract method can be specified to represent a Lambda expression.

Note the distinction between function descriptors and method descriptors, which are JVM concepts that describe the properties of a method, including the number of arguments, parameter types, and return values of the method. Such as (Ljava/lang/StringI) V

Why create functional interfaces?

Java designers also considered adding “function types” to Java to pass method references and Lambda expressions, but that was too expensive, and since Java engineers were familiar with interface tools, they used interfaces to pass lambdas and method references. The interface used to pass Lambda is called a functional interface.

@FunctionalInterface

The identifier of a FunctionalInterface is @functionalinterface, which is annotated above the interface to indicate that it is a FunctionalInterface. But in fact, as long as there is only one abstract method, the interface is a functional interface, it can represent a Lambda, and it can work without the annotation.

So the main purpose of this annotation is to inform the programmer and to inform the compiler. When programmers see this annotation, they realize that this is a functional interface. When the compiler recognizes this annotation, it verifies that there is only one abstract method in the interface. If not, it reports an error and rejects compilation:

or

A purely functional interface

The idea of functional programming is “no side effects” and “immutability.” A method that modifies neither the state of an input parameter, its owning object, nor the state of a program’s global variables is either existential or side-effect-free.

A stored function is the pursuit and guiding philosophy of functional programming. For a stored function, any use of it is safe, whether synchronous or parallel, because it cannot change the state of any other object.

Therefore, if a functional interface is an existential functional interface, then we can use it without thinking about any thread-safety, variable escape issues, and can program in the most comfortable way.

Throwing an exception is also a change in the caller’s state, so a function that throws an exception is not a function that is stored.

But this is just a guideline, not a rule, because Java’s “everything is an object” mentality is in conflict with “existential functions” in most cases. Our goal is to leverage both so that we can use better programming strategies. Instead of denying B for the sake of A.

A functional interface provided in Java

Java provides a default functional interface for almost every form of Lambda signature you can think of, documented and listed here for easy reference.

Java provides functional interfaces in the java.util. Function package. Note that each interface’s abstract function describes its Lambda signature, but this description is informal. You can describe it however you want, as long as you include a list of arguments and return values.

The code block Runnable

Runnable stands for runable. This interface is used to represent a function with no parameters and no return value.

Void run() void run() -> {… }, simply execute the code in the function.

public interface Runnable {
    public abstract void run(a);
}
Copy the code

Use:

// Thread::Thread(Runnable target)
new Thread(() -> System.out.println("HELLO WORLD")).start();
Copy the code

Verb Predicate

A predicate

Predicate means Predicate. Predicates are expressed for judgment and can be analogous to predicates in grammar, such as is, is not, yes, and no. This interface is used to represent a function that evaluates parameters.

Boolean test(T T) input T object, output Boolean, Lambda signature is T -> {… Return Boolean}, return true if t meets the condition, false otherwise.

public interface Predicate<T> {
    boolean test(T t);

    // Create a chain of functions for the Predicate, which is the sum of another Predicate
    default Predicate<T> and(Predicate<? super T> other) {}
    
    // Create the opposite logical Predicate
    default Predicate<T> negate(a) {}

    // Create a chain of functions for the Predicate, which acts as an or with another Predicate
    default Predicate<T> or(Predicate<? super T> other) {}
    
    /** * creates a Predicate function, which is a congruent function that determines if the input parameter is equal to targetRef **@paramTargetRef Judgment target * of the congruent function@param<T> Any type *@returnAn Equal comparison function */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null== targetRef) ? Objects::isNull : object -> targetRef.equals(object); }}Copy the code

Use:

// ArrayList<E>::removeIf(Predicate<? super E> filter)
arrayList.removeIf(str -> str.length() > 42);

Predicate<Object> equalWithHelloWorld = Predicate.isEqual("HELLO WORLD");
arrayList.removeIf(equalWithHelloWorld);
Copy the code
Binary predicate

BiPredicate is also a predicate function, but it can take two operands. Bi in the name means “double,” which is “binary” for a function. This interface is used to represent a function that combines two arguments.

Boolean test(T T, U U) input T object and U object, output a Boolean, Lambda signature is (T, U) -> {… Return Boolean}, return true if t and u meet the criteria, false otherwise.

public interface BiPredicate<T.U> {
    boolean test(T t, U u);
    
    // Create a chain of functions called BiPredicate, which is equivalent to the other BiPredicate
    default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {}

    // Create BiPredicate with opposite logic
    default BiPredicate<T, U> negate(a) {}

    // Creates a chain of functions for the BiPredicate, which is equivalent to the other BiPredicate
    default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other) {}}Copy the code

Use:

BiPredicate<String, String> biPredicate = String::equals;
biPredicate.test("HELLO"."WORLD");
Copy the code
Specialized predicate
  • DoublePredicate Boolean test(double value) Predicate for judging double data

  • IntPredicate Boolean test(int value) Predicate for determining int data

  • LongPredicate Boolean test(Long value) Predicate for determining long data

Each specialized Predicate has default methods for the three constructor chains of AND, OR, and negate.

Consumers Consumer

Dollar consumer

Consumer stands for “Consumer,” and this interface is used to represent a function that handles parameters.

Void accept(T T) input T object, no output, Lambda signature T -> {… }, which is used to process t, or of course, do nothing at all.

public interface Consumer<T> {
    void accept(T t);

    // Create a Consumer function chain to continue processing t after the current Consumer processing ends.
    default Consumer<T> andThen(Consumer<? super T> after) {}}Copy the code

Use:

// ArrayList<E>::forEach(Consumer<? super E> action)
arrayList.forEach(str -> System.out.println(str));
arrayList.forEach(System.out::println);
Copy the code
Binary consumer

BiConsumer is also a consumer, but operates on two operands. This interface is used to represent a function that processes two input parameters.

Void accept(T T, U U); void accept(T T, U U); }, for processing t and u.

public interface BiConsumer<T.U> {
    void accept(T t, U u);

    // Create a BiConsumer function chain to continue processing t and u after the current BiConsumer processing ends.
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {}}Copy the code

Use:

// HashMap<K, V>::forEach(BiConsumer<? super K, ? super V> action)
hashMap.forEach((key, value) -> System.out.println(key + "= >" + value));
Copy the code
Specialized consumer
  • DoubleConsumer void Accept (Double Value) Specifies the consumer that consumes the double type

  • IntConsumer void Accept (int value) Specifies the consumer that consumes an int

  • LongConsumer void Accept (Long Value) Specifies the consumer that consumes the long type

  • ObjDoubleConsumer void Accept (T T, double Value) specifically consumes binary consumers of normal objects and double types

  • ObjIntConsumer void Accept (T T, int value

  • ObjLongConsumer void Accept (T T, long Value) specifically consumes binary consumers of normal objects and types of long

Function is the Function

Function features: given an input data set, will output the results of operations, there are inputs and output is the function.

A function of

Function is a Function, but in this case is the most common unary Function. This interface is used to represent a Function that processes input parameters and returns the results.

R apply(T T) input T type objects, output R type objects, Lambda signature is T -> {… Return r}, used to convert t to data of type R.

public interface Function<T.R> {
    R apply(T t);
    
    
      
        + 
       
         == 
        ,>
       ,>
      ,>
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {}
    
    
      
        and 
       
         == 
        ,>
       ,>
      ,>
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {}
    
    /** * create a Function that does nothing but return whatever is typed **@param<T> Any type *@returnA function that returns input data directly */
    static <T> Function<T, T> identity(a) {
        returnt -> t; }}Copy the code

Use:

// HashMap<K, V>::computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
hashMap.computeIfAbsent("ABC", strKey -> strKey.length());
hashMap.computeIfAbsent("ABC", String::length);
Copy the code
Dual function

BiFunction is used to represent a function that processes two input parameters and returns the result.

R apply(T T, U U) input T objects and U objects, output R objects, Lambda signature is (T, U) -> {… Return r}, used to convert t and u to r type data.

public interface BiFunction<T.U.R> {
    R apply(T t, U u);

    Function, 
      
        + 
       
         == 
        ,>
       ,>
      ,>
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {}}Copy the code

Use:

// HashMap<K, V>::computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
hashMap.computeIfPresent("ABC", (strKey, intVal) -> intVal + strKey.length());
Copy the code
A specialized function
  • DoubleFunction R apply(double Value) A function that handles double data and outputs data of type R.

  • DoubleToIntFunction int applyAsInt(double value) A function that handles double data and outputs data of type int.

  • DoubleToLongFunction Long applyAsLong(double Value) A function that handles double data and outputs data of type long.

  • IntFunction R apply(int value) A function that processes int data and outputs data of type R.

  • IntToDoubleFunction Double applyAsDouble(int value) A function that handles int data and outputs data of type double.

  • IntToLongFunction Long applyAsLong(int value) A function that processes int data and outputs long data.

  • LongFunction R apply(long value) a function that processes long data and outputs data of type R.

  • LongToDoubleFunction Double applyAsDouble(long value) a function that handles long data and outputs data of type double.

  • LongToIntFunction int applyAsInt(long value) A function that processes long data and outputs int data.

  • ToDoubleFunction Double applyAsDouble(T T) A function that handles T data and outputs data of type double.

  • ToIntFunction int applyAsInt(T T) A function that handles T data and outputs int data.

  • ToLongFunction long applyAsLong(T T) a function that processes T data and outputs long data.

  • ToDoubleBiFunction double applyAsDouble(T T, U U) a function that handles T and U data and outputs double data.

  • ToIntBiFunction int applyAsInt(T T, U U) a function that handles T and U data and outputs int data.

  • ToLongBiFunction long applyAsLong(T T, U U) a function that processes T and U data and outputs long data.

Operator Operator

All operands are of the same type as the result of a special function. Therefore, the Operator can inherit Function, and of course not (youdaoplaceholder0) ~→.

Unary operator

Unary in UnaryOperator stands for “Unary,” and this interface represents a function that processes input parameters and returns data of the same type.

T apply(T T) inherits Function. It inputs T objects and outputs data of type T. Lambda signature is t -> {… Return t/newT}, returns the same result as the argument type.

public interface UnaryOperator<T> extends Function<T.T> {
    /** * Creates an UnaryOperator that returns the input argument **@param<T> Any type *@returnA function with the same input and output */
    static <T> UnaryOperator<T> identity(a) {
        returnt -> t; }}Copy the code

Use:

UnaryOperator<Integer> kbToMb = t -> t << 10;
// ArrayList::replaceAll(UnaryOperator<E> operator)
arrayList.replaceAll(kbToMb);
Copy the code
Binary operator

BinaryOperator is used to represent a function that processes two input parameters of the same type and returns a result of the same type.

T apply(T T1, T T2) inherited from BiFunction, input two T objects, output T type data, Lambda signature is (T1, T2) -> {… Return t3}, which processes T1 and T2 to t3.

public interface BinaryOperator<T> extends BiFunction<T.T.T> {
    /** * Creates a BinaryOperator that returns the smallest of two arguments, according to the given collation rule@paramComparator collation *@param<T> Any type *@returnA function that outputs the minimum */
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    /** * Creates a BinaryOperator that returns the larger of the two arguments ** according to the given collation rules@paramComparator collation *@param<T> Any type *@returnA function that outputs the maximum value */
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0? a : b; }}Copy the code

Use:

BinaryOperator<String> minString = BinaryOperator.minBy(String::compareTo);
// HashMap<K, V>::computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
hashMap.computeIfPresent("ABC", minString);
Copy the code
Specialization operator
  • DoubleUnaryOperator Double applyAsDouble(double operand) is used exclusively to operate on unary operators of type double.

  • IntUnaryOperator int applyAsInt(int operand) Is used to operate unary operators of type INT.

  • LongUnaryOperator Long applyAsLong(long Operand) is dedicated to operating on unary operators of type long.

  • DoubleBinaryUnaryOperator double applyAsDouble (double left, double right) is dedicated to the operation of type double unary operator.

  • IntBinaryUnaryOperator int applyAsInt(int left, int right) is used to operate on unary operators of type INT.

  • LongBinaryUnaryOperator Long applyAsLong(Long left, Long Right) is dedicated to operating on unary operators of type long.

Supply: Supplier

The characteristic of a provider is that it does not require any input data, and the provider provides data outward, that is, output data outward.

Supplier can supply any type of data externally. This interface represents a function that provides some type of data.

T get() has no input, outputs data of type T, Lambda signature is () -> {… Return t}, which generates data of type T according to the logic inside the function.

public interface Supplier<T> {
    T get(a);
}
Copy the code

Use:

String string = randomBoolean.getAsBoolean() ? null : "HELLO WORLD";
Supplier<String> stringSupplier = () -> String.valueOf(System.currentTimeMillis());
// Objects::requireNonNull(T obj, Supplier<String> messageSupplier)
String s = Objects.requireNonNull(string, stringSupplier);
Copy the code
Specialized provider
  • BooleanSupplier boolean getAsBoolean(); A provider specialized in providing Boolean data.

  • DoubleSupplier double getAsDouble(); A provider dedicated to providing double data.

  • IntSupplier int getAsInt(); A specialized provider for int data.

  • LongSupplier long getAsLong(); A provider dedicated to providing long data.

Functional interface summary

  • No parameter no return value

    Function return arg1 arg2
    Runnable::run void
  • The single parameter has no return value

    Function return arg1 arg2
    Consumer::accept void <T>
    DoubleConsumer::accept void double
    IntConsumer::accept void int
    LongConsumer::accept void long
  • No return value for double parameters

    Function return arg1 arg2
    BiConsumer::accept void <T> <U>
    ObjDoubleConsumer::accept void <T> double
    ObjIntConsumer::accept void <T> int
    ObjLongConsumer::accept void <T> long
  • No parameter has a return value

    Function return arg1 arg2
    Supplier::get <T>
    BooleanSupplier::getAsBoolean boolean
    DoubleSupplier::getAsDouble double
    IntSupplier::getAsInt int
    LongSupplier::getAsLong long
  • The single parameter has a return value

    Function return arg1 arg2
    Predicate::test boolean <T>
    DoublePredicate::test boolean double
    IntPredicate::test boolean int
    LongPredicate::test boolean long
    Function::apply <R> <T>
    DoubleFunction::apply <R> double
    DoubleToIntFunction::applyAsInt int double
    DoubleToLongFunction::applyAsLong long double
    IntFunction::apply <R> int
    IntToDoubleFunction::applyAsDouble double int
    IntToLongFunction::applyAsLong long int
    LongFunction::apply <R> long
    LongToDoubleFunction::applyAsDouble double long
    LongToIntFunction::applyAsInt int long
    ToDoubleFunction::applyAsDouble double <T>
    ToIntFunction::applyAsInt int <T>
    ToLongFunction::applyAsLong long <T>
    UnaryOperator::apply <T> <T>
    DoubleUnaryOperator::applyAsDouble double double
    IntUnaryOperator::applyAsInt int int
    LongUnaryOperator::applyAsLong long long
  • The double parameter has a return value

    Function return arg1 arg2
    BiFunction::apply <R> <T> <U>
    ToDoubleBiFunction::applyAsDouble double <T> <U>
    ToIntBiFunction::applyAsInt int <T> <U>
    ToLongBiFunction::applyAsLong long <T> <U>
    BinaryUnaryOperator::apply <T> <T> <T>
    BinaryUnaryOperator::applyAsDouble double double double
    BinaryUnaryOperator::applyAsInt int int int
    BinaryUnaryOperator::applyAsLong long long long