文章目录
1. Lambda简介
Lambda表达式是JDK8的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java代码,尤其在集合遍历和其它集合操作中,可以极大地优化代码结构。
1.1 对接口的要求
Lambda规定接口中只能有一个需要被实现的方法,不是规定接口中只有一个方法
JDK8中有另外一个新特性:default,被default修饰的方法会有默认实现,不是必须被实现的方法,所以不影响Lambda表达式的使用。
1.2 什么是函数式接口
在Java中,标记类型的接口是一种没有方法或属性声明的接口,简单地说,标记接口是空接口,相似地,函数式接口是只包含一个抽象方法声明的接口。
@FunctionalInterface
是Java8新加入的一种接口,用于注明该接口类型声明是根据Java语言规范定义的函数式接口。Java8还声明了一些Lambda表达式可以使用的函数式接口,当你注释的接口不是有效的函数式接口时,可以使用@FunctionalInterface
解决编译层面的错误。
2. Lambda结构
- 一个Lambda表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如(String s)与(s)相同
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如(a, b)或(int a, int b)
- 空圆括号代表参数集为空
- 当只有一个参数,且其类型可推导时,圆括号可省略
- Lambda表达式的主体可包含零条或多条语句
- 若Lambda表达式的主体只有一条语句花括号{}可省略。匿名函数的返回类型与该主体表达式相同
- 若Lambda表达式的主体包含一条以上语句,则表达式必须包含在花括号{}之中,匿名函数的返回类型与该代码块返回值相同
3. Lambda表达式基础语法
我们先定义6个接口类,后面的操作实例均在此基础上进行。
//多参数无返回
@FunctionalInterface
public interface NoReturnMultiParam {
void method(int a, int b);
}
//无参无返回值
@FunctionalInterface
public interface NoReturnNoParam {
void method();
}
//一个参数无返回
@FunctionalInterface
public interface NoReturnOneParam {
void method(int a);
}
//多个参数有返回值
@FunctionalInterface
public interface ReturnMultiParam {
int method(int a, int b);
}
//无参有返回
@FunctionalInterface
public interface ReturnNoParam {
int method();
}
//一个参数有返回值*/
@FunctionalInterface
public interface ReturnOneParam {
int method(int a);
}
语法形式为() -> {}
- ()用来描述参数列表
- {}用来描述方法体
- ->为lambda运算符,读作(goes to)
示例:
public class LambdaTest {
public static void main(String[] args) {
//无参无返回
NoReturnNoParam noReturnNoParam = () -> {
System.out.println("无参无返回");
};
noReturnNoParam.method();
//一个参数无返回
NoReturnOneParam noReturnOneParam = (int a) -> {
System.out.println("一个参数无返回:" + a);
};
/**简化版
NoReturnOneParam noReturnOneParam = a -> {
System.out.println("一个参数无返回:" + a);
};
*/
noReturnOneParam.method(666);
//多个参数无返回
NoReturnMultiParam noReturnMultiParam = (int a, int b) -> {
System.out.println("多个参数无返回:{" + a + "," + b + "}");
};
/**简化版
NoReturnMultiParam noReturnMultiParam = (a, b) -> {
System.out.println("多个参数无返回:{" + a + "," + b + "}");
};
*/
noReturnMultiParam.method(1, 1);
//无参有返回
ReturnNoParam returnNoParam = () -> {
System.out.println("无参有返回");
return 1;
};
int res = returnNoParam.method();
System.out.println("无参有返回的返回值:" + res);
//一个参数有返回值
ReturnOneParam returnOneParam = (int a) -> {
System.out.println("一个参数有返回值:" + a);
return 1;
};
/**简化版
ReturnOneParam returnOneParam = a -> {
System.out.println("一个参数有返回值:" + a);
return 1;
};
*/
res = returnOneParam.method(666);
System.out.println("一个参数有返回值的返回值:" + res);
//多个参数有返回值
ReturnMultiParam returnMultiParam = (int a, int b) -> {
System.out.println("多个参数有返回值:{" + a + "," + b + "}");
return 1;
};
/**简化版
ReturnMultiParam returnMultiParam = (a, b) -> {
System.out.println("多个参数有返回值:{" + a + "," + b + "}");
return 1;
};
*/
res = returnMultiParam.method(1, 2);
System.out.println("多个参数有返回值的返回值:" + res);
ReturnMultiParam lambda1 = (a, b) -> a + b;
ReturnMultiParam lambda2 = Integer::sum;
System.out.println(lambda1.method(1, 1));
System.out.println(lambda2.method(2, 2));
}
}
4.Lambda表达式常用示例
4.1 Lambda表达式引用方法
有时我们不是必须要自己重写某个匿名内部类的方法,我们可以利用Lambda表达式的接口快速指向一个已经被实现的方法。
语法:
方法归属者::方法名
静态方法的归属者为类名,普通方法的归属者为对象
public class LambdaTest2 {
public static void main(String[] args) {
ReturnOneParam lambda3 = a -> doubleNum(a);
System.out.println(lambda3.method(2));
//lambda4 引用了已经实现的 doubleNum 方法
ReturnOneParam lambda4 = LambdaTest::doubleNum;
System.out.println(lambda4.method(2));
LambdaTest lambdaTest = new LambdaTest();
//lambda5 引用了已经实现的 addTwo 方法
ReturnOneParam lambda5 = lambdaTest::addTwo;
System.out.println(lambda5.method(3));
}
/**
* 要求
* 1.参数数量和类型要与接口中定义的一致
* 2.返回值类型要与接口中定义的一致
*/
public static int doubleNum(int a) {
return a * 2;
}
public int addTwo(int a) {
return a + 2;
}
}
4.2 构造方法的引用
一般我们需要声明接口,该接口作为对象的生成器,通过类名::new 的方式来实例化对象,然后调用方法返回对象。
先创建个实体类供之后使用:
public class Item {
private int id;
private String name;
private int price;
public Item() {
}
public Item(int id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
示例:
public class LambdaTest3 {
interface ItemCreatorBlankConstruct {
Item getItem();
}
interface ItemCreatorParamConstruct {
Item getItem(int id, String name, int price);
}
public static void main(String[] args) {
ItemCreatorBlankConstruct construct = () -> new Item();
Item item = construct.getItem();
ItemCreatorBlankConstruct construct2 = Item::new;
Item item2 = construct2.getItem();
ItemCreatorParamConstruct construct3 = Item::new;
Item item3 = construct3.getItem(11, "macbook pro", 8999);
}
}
4.3 Lambda表达式创建线程
以往我们创建线程都是通过创建Thread对象,然后通过匿名内部类重写run()方法,到这里看到匿名内部类就应该可以想到使用Lambda表达式来简化线程创建过程。
//旧方法:
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程:" + i);
}
}
}).start();
//新方法
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("线程:" + i);
}
}).start;
4.4 遍历集合
我们可以调用集合的public void forEach(Consumer<? super E> action)
方法,通过Lambda表达式的方式遍历集合中的元素,Consumer 接口是 JDK 为我们提供的一个函数式接口。
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,2,3,4,5);
//旧方法
for(Integer n: list) {
System.out.println(n);
}
//新方法
list.forEach(n -> System.out.println(n));
//或者使用引用
list.forEach(System.out::println);
4.5 删除集合中的某个元素
我们通过public boolean removeIf(Predicate<? super E> filter)
方法来删除集合中的某个元素,Predicate 也是JDK 为我们提供的一个函数式接口,可以简化程序的编写。
ArrayList<Item> items = new ArrayList<>();
items.add(new Item(3, "牙刷", 1));
items.add(new Item(2, "牙膏", 3));
items.add(new Item(1, "牙缸", 5));
items.add(new Item(4, "假牙", 2000));
items.add(new Item(5, "冲牙器", 100));
//旧方法
for (Item item : items) {
if (item.getId() == 4){
items.remove(item);
}
}
//新方法
items.removeIf(ele -> ele.getId() == 4);
items.forEach(System.out::println);
4.6 集合内元素的排序
在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。
//旧方法
items.sort(new Comparator<Item>() {
@Override
public int compare(Item o1, Item o2) {
return o1.getId() - o2.getId();
}
});
//新方法
items.sort((o1,o2) -> o1.getId() - o2.getId());
items.forEach(System.out::println);
4.7 Lambda表达式中的闭包问题
这个问题我们在匿名内部类中也会存在,如果我们把注释放开就会报错,告诉我们num值是final不能被改变。这里我们虽然没有标识 num 类型为 final,但是在编译期间虚拟机会帮我们加上 final 修饰关键字。
int num = 10;
Consumer<String> consumer = ele -> {
System.out.println(num);
};
// num = num + 2;
consumer.accept("hello");