Java8 Lambda 基础之函数接口
什么是函数接口?
函数接口英文全称是FunctionalInterface,是一种可用于Lambda表达式的接口,该概念在JDK 8首次被提出,关于FunctionalInterface JDK官方文档的解释是
* Conceptually, a functional interface has exactly one abstract
* method. Since {@linkplain java.lang.reflect.Method#isDefault()
* default methods} have an implementation, they are not abstract. If
* an interface declares an abstract method overriding one of the
* public methods of {@code java.lang.Object}, that also does
* <em>not</em> count toward the interface’s abstract method count
* since any implementation of the interface will have an
* implementation from {@code java.lang.Object} or elsewhere.
从中我们得知一个functional interface 有且只有一个抽像方法。比如我们常见的Runnable接口
@FunctionalInterface
public interface Runnable {
/** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */
public abstract void run();
}
但functional interface中并不限制有非抽像方法存在比如Java8 后引进的default方法。以Stream.forEach中常用的Consumer接口为例,里面就带了一个andThen的非抽像方法。
@FunctionalInterface
public interface Consumer<T> {
/** * Performs this operation on the given argument. * * @param t the input argument */
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
除此之外functional interface还不限制存在Object类中定义的公有接口的方法,因为这本来就是Interface中隐式声明的。即使显示声明也不会计算进抽像方法中。比如JDK自带的Comparator里面就定义了两个抽像方法,但因为equals是Object类公有的方法。所以equals方法不会被计算进抽像方法个数。Comparator依然是个function interface。
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
所以总的来说,函数接口是只有一个抽像方法,但可以抽像定义Object类公有方法和存在default方法的一类接口。
创建实例
一般接口实例的创建是implement该接口的对象,函数接口也不例外。但函数接口的特别之外在于Lambda表达式方面的应用,它可以被lambda expression、method references、 constructor references创建。可以说lambda表礞式创建的所有对象都是函数接口实例。
下面是三种创建的例子
Runnable runnable = () -> System.out.println("lambda expression"); //lambda expression Comparator<String> comparing = Comparator.comparing(String::length); //method references BiConsumer<File, String> fileStringBiConsumer = File::new; //constructor references
另外函数接口之所以限制只有一个抽像方法的原因,是因为这个抽像方法代表了lambda expression要实现的函数类型。如果有多个抽像方法lambda expression根本不知道实现的是哪个方法。另外抽像方法的参数个类、参数类型以及返回值、异常定义是编译器进行类型推断的依据。
泛型问题
比如有以下三个接口:
@FunctionalInterface
public interface Foo extends Foo1, Foo2 {
void bar(List args);
}
interface Foo1 {
void bar(List<String> args);
}
interface Foo2 {
void bar(List<Integer> args);
}
看上去Foo 继承了Foo1, Foo2两个接口。这两个接口又各自了一个抽像方法,Foo又定义了一个抽像方法。这样Foo就有三个抽像方法了,不可能是一个funtion interface。但实际上因为Java泛型会有一个类型擦除动作。所以这三个接口的方法实际都是:
void bar(List args);
这样Foo的抽像方法相当于覆盖了Foo1,Foo2的接口中的抽像方法,就是只有一个。所以Foo可以看作是function inferface。
原文地址: https://zhuanlan.zhihu.com/p/58586114
本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
相关文章