【Java8】函数式接口

摘要

  • 何为函数式接口?
  • 什么是lambda表达式,lambda表达式的本质;
  • 函数式接口与lambda表达式的联系:lambda是实现函数式接口的一个快捷方式,可以作为函数式接口的一个实例;
  • 常用Java8内置的函数式接口 Function、Predicate、Consumer 和 Supplier 介绍;

函数式接口

何为函数式接口?
函数式接口也是 java interface 的一种,但还需要满足:

  • 一个函数式接口只有一个抽象方法(SAM,single abstract method);
  • 函数式接口可以有默认方法和静态方法;
  • 函数式接口可以用@FunctionalInterface 注解进行修饰。

jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。

满足这些条件的 interface,就可以被视为函数式接口。例如,java8中的Comparator接口:

@FunctionalInterface
public interface Comparator<T> {
    /**
     * single abstract method
     * @since 1.8
     */
    int compare(T o1, T o2);

    /**
     * Object 类中的 public abstract method 
     * @since 1.8
     */
    boolean equals(Object obj);

    /**
     * 默认方法
     * @since 1.8
     */
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

	......
    
	/**
     * 静态方法
     * @since 1.8
     */
    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }

	.....
}

函数式接口有什么用?

一句话,函数式接口带给我们最大的好处就是:可以使用极简的lambda表达式实例化接口。

原始匿名内部类方式:

class Test { 
    public static void main(String args[]) { 
        List<Person> persons = new ArrayList<Person>();
        Collections.sort(persons, new Comparator<Person>(){
            @Override
            public int compare(Person o1, Person o2) {
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        });
    } 
} 

lambda简化后:

Comparator<Person> comparator = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());

常用的函数式接口

函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 这类接口只定义了唯一的抽象方法的接口,并且这类接口使用了@FunctionalInterface进行注解。在jdk8中,引入了一个新的包java.util.function, 可以使java 8 的函数式编程变得更加简便。这个package中的接口大致分为了以下四类:

  • Function: 接收参数,并返回结果,主要方法 R apply(T t)
  • Consumer: 接收参数,无返回结果, 主要方法为 void accept(T t)
  • Supplier: 不接收参数,但返回结构,主要方法为 T get()
  • Predicate: 接收参数,返回boolean值,主要方法为 boolean test(T t)

java.util.function包中所有的接口整理:
img

Lambda 基础语法

语法形式为() -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

Lambda 语法简化

我们可以通过观察以下代码来完成代码的进一步简化,写出更加优雅的代码。

Copyimport lambda.interfaces.*;

public class Test2 {
    public static void main(String[] args) {

        //1.简化参数类型,可以不写参数类型,但是必须所有参数都不写
        NoReturnMultiParam lamdba1 = (a, b) -> {
            System.out.println("简化参数类型");
        };
        lamdba1.method(1, 2);

        //2.简化参数小括号,如果只有一个参数则可以省略参数小括号
        NoReturnOneParam lambda2 = a -> {
            System.out.println("简化参数小括号");
        };
        lambda2.method(1);

        //3.简化方法体大括号,如果方法条只有一条语句,则可以胜率方法体大括号
        NoReturnNoParam lambda3 = () -> System.out.println("简化方法体大括号");
        lambda3.method();

        //4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号
        ReturnOneParam lambda4 = a -> a+3;
        System.out.println(lambda4.method(5));

        ReturnMultiParam lambda5 = (a, b) -> a+b;
        System.out.println(lambda5.method(1, 1));
    }
}

Function

Java8 引入了函数式接口,并在java.util.function 包内预定义了常用函数式接口,下表罗列了一些常用的函数式接口:
在这里插入图片描述

/**
 * Represents a function that accepts one argument and produces a result.
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

一个实例如下:

public class FunctionTest {
    public static void main(String[] args) {
        Function<Integer, Integer> func1 = item -> item + 1;

        Function<Integer, Integer> func2 = item -> item * 3;

        System.out.println(func1.compose(func2).apply(2));	//7
        System.out.println(func1.andThen(func2).apply(2));  //9
    }
}

Predicate

Predicate接口接受一个输入参数,返回一个布尔值结果。其定义为:

/**
 * Represents a predicate (boolean-valued function) of one argument.
 * @param <T> the type of the input to the predicate
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

其默认方法也封装了and、or和negate逻辑,看个例子:

public static void main(String[] args) {
	Predicate<Person> predicate1 = (Person person) -> {
            Integer age = person.getAge();
            return age >= 18;
    };

	Predicate<Person> predicate2 = (Person person) -> {
		String name = person.getName();
		return name.startsWith("R");
	};

	Person rico = new Person();
	rico.setName("Rico");
	rico.setAge(16);

	System.out.println("Rico名字以R开头且年满18岁 : " + predicate1.and(predicate2).test(rico));
	System.out.println("Rico名字以R开头或年满18岁 : " +  predicate1.or(predicate2).test(rico));
	System.out.println("Rico名字不是以R开头 : " +  predicate1.negate().test(rico));
}
/** output
	Rico名字以R开头且年满18岁 : false
	Rico名字以R开头或年满18岁 : true
	Rico名字不是以R开头 : true
*/

其他Predicate接口:

  • BiPredicate:boolean test(T t, U u);接受两个参数的二元谓词
  • DoublePredicate:boolean test(double value);入参为double的谓词函数
  • IntPredicate:boolean test(int value);入参为int的谓词函数
  • LongPredicate:boolean test(long value);入参为long的谓词函数

(126条消息) Java 8 Predicate花样用法_明明如月学长的博客-CSDN博客_predicate用法

Supplier

Supplier接口不需要任何参数,返回一个值。其定义为:

/**
 * Represents a supplier of results.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

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

看个例子:

public static void main(String[] args) {
	Person rico = new Person();
	rico.setName("Rico");
	rico.setAge(16);

	Supplier<Person> supplier = () -> rico;
	System.out.println("supplier : " + supplier.get());
}
/** output
	supplier : {"age":16,"name":"Rico"}
*/

其他Supplier接口:

  • BooleanSupplier:boolean getAsBoolean(); 返回boolean
  • DoubleSupplier:double getAsDouble();返回double
  • IntSupplier:int getAsInt();返回int
  • LongSupplier:long getAsLong();返回long

Consumer

Consumer接口接受一个输入参数,没有返回值。其定义为:

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *  * @param <T> the type of the input to the operation
 *  * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

示例:

public class ConsumerTest {
    public static void main(String[] args) {
        Consumer<int[]> consumer = arr -> System.out.println(Arrays.toString(arr));
        int[] arr = {1,2,3,5};
        consumer.accept(arr);	//[1, 2, 3, 5]
    }
}

其他Consumer接口:

  • BiConsumer:void accept(T t, U u);接受两个参数
  • DoubleConsumer:void accept(double value);接受一个double参数
  • IntConsumer:void accept(int value);接受一个int参数
  • LongConsumer:void accept(long value);接受一个long参数
  • ObjDoubleConsumer:void accept(T t, double value);接受一个泛型参数一个double参数
  • ObjIntConsumer:void accept(T t, int value);接受一个泛型参数一个int参数
  • ObjLongConsumer:void accept(T t, long value);接受一个泛型参数一个long参数

Lambda 表达式常用示例

Lambda 表达式中的闭包问题

这个问题我们在匿名内部类中也会存在,如果我们把注释放开会报错,告诉我 num 值是 final 不能被改变。这里我们虽然没有标识 num 类型为 final,但是在编译期间虚拟机会帮我们加上 final 修饰关键字。

Copyimport java.util.function.Consumer;
public class Main {
    public static void main(String[] args) {

        int num = 10;

        Consumer<String> consumer = ele -> {
            System.out.println(num);
        };

        //num = num + 2;
        consumer.accept("hello");
    }
}

参考文档

https://www.cnblogs.com/haixiang/p/11029639.html

https://blog.csdn.net/justloveyou_/article/details/89066782

posted @ 2022-02-23 20:36  至安  阅读(131)  评论(0编辑  收藏  举报