Java是面向对象语言,所以java对行为的封装都是基于对象。什么意思,简单说,我们要定义个行为,那么需要一个函数,只有函数就行了吗?在java里面不行,因为java是面向对象的语言,
所以我们需要定义对象,看下面的代码:
public interface ActionListener {
void actionPerformed(ActionEvent e);
}
最早接触过java swing编程的老程序员肯定不陌生,定义一个按钮触发时间,每当时间发生时,调用这个函数actionPerformed里面逻辑。
其实大家发现没有,这个类ActionListener其实完全没有必要。所以出现了匿名内部类。改进的代码如下:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.dazzle(e.getModifiers());
}
});
后来我们一直用匿名内部类定义行为,不用去定义对象了。但是仍然有问题,主要存在以下问题:
this
和变量名容易使人产生误解final
的局部变量随着回调的大量使用和函数式编程的流行,java需要一种便捷的方式匿名内部类。
对于上面的,只有一个抽象方法的接口,我们定义为函数式接口。只有一个方法的接口,在jdk里面很多,比如Runnable,Compartor等。看下面的源码:
package java.lang;
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
可以通过 @FunctionalInterface
注解来显式指定一个接口是函数式接口(以避免无意声明了一个符合函数式标准的接口),加上这个注解之后,编译器就会验证该接口是否满足函数式接口的要求。
Java SE 8中增加了一个新的包:java.util.function
,它里面包含了常用的函数式接口,例如:
Predicate
——接收 T
并返回 boolean
Consumer
——接收 T
,不返回值Function
——接收 T
,返回 R
Supplier
——提供 T
对象(例如工厂),不接收值UnaryOperator
——接收 T
对象,返回 T
BinaryOperator
——接收两个 T
,返回 T
除了上面的这些基本的函数式接口,我们还提供了一些针对原始类型(Primitive type)的特化(Specialization)函数式接口,例如 IntSupplier
和 LongBinaryOperator
。
看下面的代码:
//使用com.google.guava包创建集合
List list =Lists.newArrayList("a","b","c","d");
//1、正常遍历
list.forEach(item->System.out.println(item));
//2、根据条件遍历
list.forEach(item->{
if("b".equals(item)){
System.out.println(item);
}
foreach里面利用的就是lamabda表达式,我们看看foreach的源代码:
public interface Iterable {
Iterator iterator();
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
foreach的入参是Consumer类型,再看Consumer是个函数式接口,不需要指定方法,因为函数式接口只有一个方法。
函数式接口可以被隐式地转换为lamadba表达式
下面的图片来自:https://www.cnblogs.com/PomeloYe/p/11807059.html
这样,我们就成功的非常优雅的把"一块代码"赋给了一个变量。而"这块代码",或者说"这个被赋给一个变量的函数",就是一个Lambda表达式。
但是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是"那段代码",需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:
我们看一下有返回值的情况,函数式接口返回值,其实是在函数式接口定义的,我们看compartor类:
@FunctionalInterface
public interface Comparator {
使用方式如下:
Comparator byNameLambda =
(Developer developer, Developer compareDeveloper)->developer.getName().compareTo(compareDeveloper.getName());
或者:
Comparator byNameLambdaSimple = Comparator.comparing(Developer::getName);