JAVA系列---函数式接口

函数式接口的定义

  1. 一个函数式接口有且仅有一个抽象方法(SAM,single abstract method)。对于接口来说抽象方法必须重写,默认方法可选重新,静态方法不可重新。
  2. Object 类中的 public abstract method 不会被视为单一的抽象方法。这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法)。因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现。
  3. 函数式接口可以用@FunctionalInterface 注解进行修饰。但并不是必须用注解标注,一个接口只要满足只有一个抽象方法的条件就可以称为函数式接口。
    如果使用了此注解,再往接口中新增抽象方法,编译器就会报错,编译不通过。换句话说,@FunctionalInterface就是一个承诺,承诺该接口世世代代都只会存在这一个抽象方法。因此,凡是使用了这个注解的接口,开发者可放心大胆的使用lambda来实例化。当然误用@FunctionalInterface带来的后果也是极其惨重的:如果哪天你把这个注解去掉,再加一个抽象方法,则所有使用lambda实例化该接口的客户端代码将全部编译错误。所以当某接口只有一个抽象方法,但没有增加@FunctionalInterface,则代表该接口不承诺未来不增加抽象方法,所以建议不要用lambda来实例化,还是老老实实的用以前的方式比较稳妥。

函数式接口的示例

@FunctionalInterface
public interface HelloWorldService {
	
    void sayHello(String msg);
    
    default void doSomeWork1() {
        // Method body
    }

    default void doSomeWork2() {
        // Method body
    }
    static void printHello() {
        System.out.println("Hello");
    }
    
	@Override
    boolean equals(Object obj);

}

函数式接口的用处

函数式接口带给我们最大的好处就是:可以使用极简的lambda表达式实例化接口。为什么这么说呢?我们或多或少使用过一些只有一个抽象方法的接口,比如 Runnable, ActionListener, Comparator …。比如,我们要用Comparator实现排序算法,我们的处理方式通常无外乎两种:

  • 规规矩矩的写一个实现Comparator接口的java类去封装排序逻辑。若业务需要多种排序方式,那就得写多个类提供多种实现,而这些实现往往只需使用一次
  • 另外一种聪明些的做法无外乎就是在需要的地方搞个匿名内部类。比如:
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());
            }
        });
    } 
} 

匿名内部类实现的代码量没有多到哪里去,结构也还算清晰。不过如今脚本语言横行无阻,无不以其简洁的语法为杀手锏,俘获程序员的欢心。jdk开发者们应该是意识到了这一点,我们从上面Comparator的实现就可以得到验证。该接口在jdk8的实现增加了FunctionalInterface注解,代表Comparator是一个函数式接口,使用者可放心的通过lambda表达式来实例化。那我们来看看使用lambda实例化所需编写的代码:

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

lambda表达式

lambda这个名称是由美国数学家 Alonzo Church选择的,他在 1930 年代发展计算理论时需要一个希腊字母来表示函数抽象的运算符(λ)。所以其实没什么特殊意义,对于名称纠结症的可以放一放 了。

lambda表达式其实是快速创建函数式接口实例的语法糖; -> 就是lambda的语法标志。

lambda是什么

lambda表达式是实现函数式接口的一个快捷方式。下面这段代码是最直观的体现:

Runnable task = () -> System.out.println("i am coming...");

=号的前面是变量的声明,后面是一个lambda表达式。我们直接将lambda表达式赋值为Runnable类型的task变量,就意味着:lambda表达式是实现函数式接口的一个快捷方式。理解这一点是至关重要的。

lambda表达式语法

lambda表达式语法可抽象表示为:

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
     statment1;
     statment2;
     ...
     return statmentN;
}

以Predicate判断年龄为例

Predicate<Person> predicate = (Person person) -> {
    Integer age = person.getAge();
    return age >= 18;
};

但上面的并不是我们常见的代码,那是因为我们常用的是极简风,以下是简化过程:参数类型省略-》参数占位省略-》返回语句省略。

  1. 参数类型省略:因为编译器可以从上下文环境中推断出lambda表达式的参数类型

    (param1, param2, ..., paramN) -> {
        statment1;
        statment2;
        ...
        return statmentN;
    }
    
    Predicate<Person> predicate = (person) -> {
        Integer age = person.getAge();
        return age >= 18;
    };
    
  2. 参数占位省略:参数占位省略存在两种情况,当无参时,必须使用小括号来占位;当有一个参数时,省略小括号即可,参数直接占位

    param -> {
        statment1;
        statment2;
        ...
        return statmentN;
    }
    
    Predicate<Person> predicate = person -> {
        Integer age = person.getAge();
        return age >= 18;
    };
    
  3. 表达式省略:当表达式只包含一条语句时,可以同时省略大括号和return关键字和语句结尾的分号。但需要注意的是三者必须同时省略或者同时不省略

    param -> statment;
    
    Predicate<Person> predicate = person -> person.getAge() >= 18
    
    Predicate<Person> predicate = person -> {return person.getAge() >= 18;}
    

lambda表达式与匿名类

lambda表达式与匿名类的异同集中体现在三点上:

  • lambda就是为了优化匿名内部类而生,lambda要比匿名类简洁的多得多;
  • lambda仅适用于函数式接口,匿名类不受限;
  • 在匿名类内部,this关键字指向该抽象类实例,而lambda内部this指向闭包实例。如果需要在内部通过this关键字访问实例,必须使用匿名类;

函数式接口的使用

函数式接口为lambda表达式和方法引用(用冒号::来进行方法的调用)提供目标类型。每个功能接口都有一个抽象方法,称为该功能接口的功能方法,lambda表达式的参数和返回类型与之匹配或适配。功能接口可以在多个上下文中提供目标类型,例如赋值上下文,方法调用或强制转换上下文:

// Assignment context
Predicate<String> p = String::isEmpty;

// Method invocation context
stream.filter(e -> e.getSize() > 10)...

// Cast context
stream.map((ToIntFunction) e -> e.getSize())...

函数式接口可以使用lambda表达式,方法引用或构造函数引用创建功能接口的实例。

函数式接口有哪些

函数式接口可以对现有的函数友好地支持lambda。

JDK1.8之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

JDK1.8新增加的函数接口:

  • java.util.function包
函数接口方法名形式化描述分类用途说明
SupplierT get();() -> T供给型提供一个结果
Consumervoid accept(T t)(T) -> void消费型消费一个输出
FunctionR apply(T t)(T) -> R映射型类型转换
Predicateboolean test(T t)(T) -> boolean预测型判断输入是否符合预期
OperatorT applyAsT(T t)(T) -> T映射型数据操纵

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

示例

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
*/

其它接口

接口方法描述
BiPredicateboolean test(T t, U u);接受两个参数的二元谓词;
DoublePredicateboolean test(double value);入参为double的谓词函数;
IntPredicateboolean test(int value);入参为int的谓词函数;
LongPredicateboolean test(long value);入参为long的谓词函数;

迷糊过程

@FunctionalInterface
public interface Predicate<T> {

	boolean test(T t);
	
	default Predicate<T> and(Predicate<? super T> other) {
		Objects.requireNonNull(other);
		return (t) -> test(t) && other.test(t);
	}
}

使用:

Predicate<Integer> p = i -> i > 2;
System.out.println(p.and(i -> i < 10).test(5));

一开始不理解and方法的执行逻辑,and里面都已经调用过test方法了,为什么还能p.and.test, 原来and方法相当于二次函数式编程,and完毕之后其实相当于:

Predicate<Integer> p = i -> i > 2 && i < 10;
System.out.println(p.test(5));

函数式编程也不会改变java的核心:bean.method(param),jvm只能将bean当参数,所以jvm其实还是将Lambda表达式的结果当成对象,由此表象上看似java支持了函数语句当参数。

Function

接受一个输入参数T,返回一个结果R

/**
 * 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;
    }
}

方法表达式:

抽象方法:R=Function(T)
compose方法:V=Function(ParamFunction(T));
andThen方法:V=ParamFunction(Function(T));
identity方法:T=Function(T);

示例

public static void main(String[] args) {
	Function<Integer, Integer> func1 = x -> {
		return x + 1;
	};
	Function<Integer, Integer> func2 = y -> {
		return y * 7;
	};

	int x = 3, y = 5;
	System.out.println(func1.apply(x));
	System.out.println(func2.apply(y));

	System.out.println(func1.compose(func2).apply(x));
	System.out.println(func1.andThen(func2).apply(x));
}
/*** output
	4
	35
	22
	28
*/

其它接口

接口方法描述
BiFunctionR apply(T t, U u);接受两个参数,返回一个值,代表一个二元函数;
DoubleFunctionR apply(double value);只处理double类型的一元函数;
IntFunctionR apply(int value);只处理int参数的一元函数;
LongFunctionR apply(long value);只处理long参数的一元函数;
ToDoubleFunctiondouble applyAsDouble(T value);返回double的一元函数;
ToDoubleBiFunctiondouble applyAsDouble(T t, U u);返回double的二元函数;
ToIntFunctionint applyAsInt(T value);返回int的一元函数;
ToIntBiFunctionint applyAsInt(T t, U u);返回int的二元函数;
ToLongFunctionlong applyAsLong(T value);返回long的一元函数;
ToLongBiFunctionlong applyAsLong(T t, U u);返回long的二元函数;
DoubleToIntFunctionint applyAsInt(double value);接受double返回int的一元函数;
DoubleToLongFunctionlong applyAsLong(double value);接受double返回long的一元函数;
IntToDoubleFunctiondouble applyAsDouble(int value);接受int返回double的一元函数;
IntToLongFunctionlong applyAsLong(int value);接受int返回long的一元函数;
LongToDoubleFunctiondouble applyAsDouble(long value);接受long返回double的一元函数;
LongToIntFunctionint applyAsInt(long value);接受long返回int的一元函数;

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 static void main(String[] args) {
	Person rico = new Person();
	rico.setName("Rico");
	rico.setAge(16);

	Consumer<Person> consumer = person -> System.out.println(person);
    consumer.accept(rico);
}/*** output
	{"age":16,"name":"Rico"}
*/

其它接口

接口函数描述
BiConsumervoid accept(T t, U u);接受两个参数
DoubleConsumervoid accept(double value);接受一个double参数
IntConsumervoid accept(int value);接受一个int参数
LongConsumervoid accept(long value);接受一个long参数
ObjDoubleConsumervoid accept(T t, double value);接受一个泛型参数一个double参数
ObjIntConsumervoid accept(T t, int value);接受一个泛型参数一个int参数

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"}
*/

其它接口

接口函数描述
BooleanSupplierboolean getAsBoolean();返回boolean
DoubleSupplierdouble getAsDouble();返回double
IntSupplierint getAsInt();返回int
LongSupplierlong getAsLong();返回long
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lipviolet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值