上一篇博客Java8函数式编程之二 : Lambda表达式 - 简书 介绍了Lambda表达式,最后也留下了一个问题,就是Lambda到底用在什么地方,以及什么是函数式接口?
还记得我们在第一篇博客中定义的这个接口吗?
public interfaceTradePredicate {
booleantest(Trade trade);
}
这就是一个函数式接口。那么怎样去鉴别一个接口是否是函数式接口呢?
简单的说:函数式接口就是只定义了一个抽象方法的接口。比如JavaAPI中的Comparator 和Runnable。
public interface Comparator{
int compare(T o1, T o2);
}
public interface Runnable{
void run();
}
————————
注意:函数式接口是只定义“一个抽象方法”的接口,只定义一个抽象方法并代表没有其他方法,后面我们知道,接口里还可以有默认方法。
————
我们也可以说,加上注解@FunctionalInterface的接口(只有一个抽象方法的接口),就是一个函数式接口。
——————
关于函数式接口,注意以下几点:
1.如果一个接口只有一个抽象方法,那么该接口就是函数式接口。
2.如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口。
3.如果某个接口只有一个抽象方法,但我们并没有给该接口声明@FunctionalInterface注解,那么编译器依旧会将其看作是函数式接口。
————————————
@FunctionalInterface
public interfaceMyInterface {
public voidtest();
publicString myString();
}
这样会报错;
Invalid'@FunctionalInterface'annotation; MyInterface is not a functional interface
——————————
但是!
@FunctionalInterface
public interfaceMyInterface {
public voidtest();
publicString toString();
}
如果是这样,就不会报错,为什么呢?
————————
我们看看Java Document文档是怎么说的:
Ifan interface declares an abstract method overriding one of the
public methods of {@codejava.lang.Object}, that also does
notcount toward the interface's abstract method count
since any implementation of the interface will have an
implementation from {@codejava.lang.Object} or elsewhere.
简单的解释就是:当你重写的方法是Object类的方法时,并不会算在“抽象方法”内。
————————————
函数式接口的实例可以通过Lambda表达式,方法引用或者构造方法引用来创建。
————————————
我们来看看使用各种方式来遍历集合,对比其特点:
public classTest {
public static voidmain(String[] args) {
List list = Arrays.asList(1,2,3,4,5,6,7,8,9);
//使用for循环
for(inti =0; i < list.size(); ++i) {
System.out.print(list.get(i));
}
//增强的for循环
for(Integer i : list) {
System.out.print(i);
}
//匿名内部类
list.forEach(newConsumer() {
@Override
public voidaccept(Integer integer) {
System.out.print(integer);
}
});
//Lambda表达式
list.forEach(i -> {
System.out.print(i);
});
}
}
————————————————————
你可能对forEach()这个方法并不熟悉,我们可以看看它的Javadoc说明。
/**
* Performs the given action for each element of the {@codeIterable}
* until all elements have been processed or the action throws an
* exception. Unless otherwise specified by the implementing class,
* actions are performed in the order of iteration (if an iteration order
* is specified). Exceptions thrown by the action are relayed to the
* caller.
通过对forEach()方法注释的理解,我们发现其执行的的是一种给定的动作。
————————
那为何不继续看看forEach()方法的实现呢?
default voidforEach(Consumer action) {
Objects.requireNonNull(action);
for(Tt :this) {
action.accept(t);
}
}
————————
问题1:
你可能会疑惑,default是什么呢?好像以前没有这个关键字,并且这个方法是有实现的。这就是我们先前说的默认方法,Java8中,接口里是可以有方法实现的,这种方法就是默认方法。这是个很好的设计,即有利于Java8的新的特性的加入,又很好的兼容了以前的Java版本。
——————————
问题2:forEach()里接收的参数类型是Consumer, 这个又是什么呢,有什么用呢?
这个当然是函数式接口,不够是Java8为我们提供的,还有类似的接口如Predicate,Function等等。在下一篇博客中将详细介绍它们。