使用场景
lambda表达式 是java8中新增的语法,体现了函数式编程
的思想,即把函数当作一个对象。相当于把一个功能(function)当作一个对象。
就像数学中 求一个数的平方加上它的两倍再加1
(功能)可以写成表达式 f(x) = =x*x + 2x +1
一样, 然后用f(x)
就可以简单表示 x*x + 2x + 1
。
然后使用场景:
最简单且容易理解的场景就是 需要实现一个接口,这个接口只有一个抽象方法, 然后把匿名类的写法改成 lambda表达式
例如在用线程的时候,我们经常需要实现 Runnable 接口, 这个接口只有一个抽象方法 run,这个时候就可以使用 lambda 表达式(具体使用如下)。 为什么是只有一个抽象方法呢?因为一个函数只对应一个功能, 就像f(x)
不能同时表示 2x+1
和 3x+1
示例: 实现一个runnable 接口 传给Thread
// 最麻烦的写法,
public class Task implement Runnable {
public void run() {
System.out.println("run");
}
}
Thread t = new Thread(new Task());
// 匿名类 (一般这个时候 IDEA 都会给出优化提示)
Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println("run");
}
};
// lambda
Thread t2 = new Thread(() -> System.out.println("run"));
所以, lambda 最大的好处就是简化代码
使用方式如下
基础版 ->
语法
(参数) -> {执行语句}
- 如果仅有一个参数, 对应的括号可以省略;
- 只有一条执行语句时, 大括号和return也可以省略;
- 参数类型可以省略
简化实现单一方法的接口的代码, 即函数式接口
.
使用示例
例如使用匿名类实现一个Runnable 接口, 传统方式如下
Runnable r = new Runable() {
public void run() {
System.out.println("do something");
}
};
而使用lambda表达式,
Runnable r = () -> System.out.println("do something");
对于带参数的接口, 如 Comparator 接口(比较两个对象的大小)
Comparator<Integer> c = new Comparator<Integer>(){
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
};
// lambda
Comparator<Integer> c = (Integer o1, Integer o2) -> o1 - o2;
Comparator<Integer> c = (o1, o2) -> o1 - o2;
因为有
Comparator
类型声明, 所以jvm知道此处lambda实现的是哪个接口, 所以参数类型可以省略, 如果直接作为方法参数, 同样由参数列表确定实现的接口.
这样的话, 代码是不是简洁了很多. 有时候代码还能更简洁
进阶版 ::
语法
类名::静态方法
类名::new
对象::非静态方法
利用已有的方法实现相同结构的函数式接口, (参数列表和返回类型相同).
注意: 引用的方法参数列表和返回类型必须和接口方法一致
使用示例
- 引用静态方法
还是实现一个Comparator的接口, 比较Integer的大小
// 使用Integer的compare方法实现comparator接口, 是不是更简单了
Comparator<Integer> c = Integer::compare;
// 这是Integer.compare的方法源码
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
其实上面的方法可以理解为, 直接在实现方法中直接调用Integer.compare的方法
Comparator<Integer> c = new Comparator<Integer>(){
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
- 实例方法
就随便写一个方法了
实现Supplier接口, 在里面调用object.toString方法
Object o = new Object();
Supplier<String> c = o::toString;
//相当于
Supplier<String> c1 = new Supplier<String>(){
public String get() {
return o.toString();
}
};
// Supplier 接口(java四个基本函数式接口之一)
public interface Supplier<T> {
T get();
}
- 构造方法
// lambda
Supplier<String> s = String::new;
// 普通形式
Supplier<String> c1 = new Supplier<String>(){
public String get() {
return new String();
}
};
有人可能会疑惑, String有那么多构造方法, 它怎么知道我是引用的无参的那个, 这是因为要实现的接口的方法就是无参的。 所以存在重载方法时,会根据实现的接口的参数和返回类型选择对应的方法.