lambda表达式是一个可传递的代码块,可以在以后执行一次或多次
1、为什么引入lambda表达式
传递代码段
将一个代码块传递到某个对象,这个代码块会在将来某个时间调用。
如果不用lambda表达式传递代码块,那必须构造一个对象,这个对象的类需要有一个方法包含所需的代码
Class LengthComparator implements Comparator{
public int compare(String first, String second){
return first.length() - second.length();
}
}
// compare这段比较的代码就是被传递,而且会被调用多次的,这里构造了对象。
// 第三部分有Arrays.sort()的源码,可以看传进去干了什么
Arrays.sort(strings, new LengthComparator());
// 使用lambda的写法
Arrays.sort(planets, (first, second) -> first.length() - second.length());
2、lambda表达式语法
参数 -> 表达式
(String first, String second) -> {
if (first.length() < second.length()) return -1;
else return 0;
}
// 如果参数类型可以推导,参数类型不需要写出
Compare comp = (first, second) -> first.length() - second.length();
// 如果参数只有一个,而且类型可以推导,可以不写括号
ActionListener listener = event -> System.out.println("xxxx" + Instant.ofEpochMilli(event.getWhen()));
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class LambdaTest
{
public static void main(String[] args)
{
String[] planets = new String[] { "Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Saturn", "Uranus", "Neptune" };
System.out.println("Sorted by length:");
Arrays.sort(planets, (first, second) -> first.length() - second.length());
System.out.println(Arrays.toString(planets));
Timer t = new Timer(1000, event ->
System.out.println("The time is " + new Date()));
t.start();
}
}
3、函数式接口
对于只有一个抽象方法的接口,需要这种接口对象时,可以提供一个lambda表达式。这种接口被称为函数式接口。
Comparable就是只有一个抽象方法
实际上,在Java中,lambda表达式所能做的也只是转换为函数式接口
Arrays.sort(planets, (first, second) -> first.length() - second.length());
// 以下为Array.sort的源码,传入了这个对象/lambda表达式,最终是为了调用实现的compare方法
public static void sort(T[] a, Comparator super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
/** To be removed in a future release. */
private static void legacyMergeSort(T[] a, Comparator super T> c) {
T[] aux = a.clone();
if (c==null)
mergeSort(aux, a, 0, a.length, 0);
else
mergeSort(aux, a, 0, a.length, 0, c);
}
private static void mergeSort(Object[] src,
Object[] dest,
int low, int high, int off,
Comparator c) {
int length = high - low;
// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
for (int i=low; i
for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
swap(dest, j, j-1);
return;
}
// Recursively sort halves of dest into src
int destLow = low;
int destHigh = high;
low += off;
high += off;
int mid = (low + high) >>> 1;
mergeSort(dest, src, low, mid, -off, c);
mergeSort(dest, src, mid, high, -off, c);
// If list is already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (c.compare(src[mid-1], src[mid]) <= 0) {
System.arraycopy(src, low, dest, destLow, length);
return;
}
// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
dest[i] = src[p++];
else
dest[i] = src[q++];
}
}
4、方法引用
如果lambda表达式涉及方法,可以把方法进行传递
方法引用指示编译器生成一个函数式接口实例,覆盖这个接口的抽象方法来调用给定的方法
Arrays.sort(strings, String::compareToIgnoreCase)等价于传递了(x,y) -> x.compareToIgnore(y)
使用的时候用::分隔方法名与对象或类名 object::instanceMethod Class::instanceMethod Class:staticMethod,这个object可以是this super这些关键字
lambda表达式的体只调用一个方法而不做其他操作时,才能把lambda表达式重写为方法引用。s -> s.length() == 0就不可以写成方法引用
5、构造器引用
与方法引用类似,方法名为new
比如 int[]::new 等价于 x -> new int[x]
ArrayList names = ...;
Stream stream = names.stream().map(Person::new);
6、变量作用域
lambda表达式可以访问外围方法或类中的变量
public static void repeatMessage(String text, int delay){
ActionListener listener = event -> {
System.out.println(text);
Tollkit.getDefaultToolkit().beep();
}
}
lambda表达式有三个部分:1、代码块 2、参数 3、自由变量的值,上面这个例子里面text就是自由变量,会被存储,不用担心外部调用后参数消失
但是lambda捕获外围作用域中变量的值,需要是不会改变的变量
lambda表达式的体与嵌套块由相同的作用域
7、lambda表达式实际运用
lambda表达式的重点是延迟执行
有这样的需求可能是因为:
在一个单独的线程中运行代码
多次运行代码
在算法的适当位置运行代码(如排序中的比较)
发生某种情况时执行代码(如点击按钮时)
仅在必要时才运行
比如要实现一个重复输出内容的方法repeat(10, () -> System.out.println("Hello World")),这一部分() -> System.out.println("Hello World")需要一个函数式接口的实现
public interface Runnable{
public void run();
}
public static void repeat(int n, Runnable action){
for (int i = 0; i < n; i++) action.run();
}
// run()里面就调用System.out.println("Hello World")
// 这里就不用实现一个Runnable类,然后再给个对象了,和Compare的例子是一样的
repeat(10, () -> System.out.println("Hello World"))