1、Lambda表达式
1.1、简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 表达式规定接口中只能有一个需要被实现的方法,而不是规定接口中只能有一个方法,被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
1.2、标准格式
(参数类型 参数名称,参数类型 参数名称,...) -> {
代码体;
}
1.3、省略规则
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果大括号内有且仅有一个语句,则可以同时省略大括号、return关键字及语句分号
1.4、使用前提(函数式接口)
- 有一个接口
- 接口中有且仅有一个抽象方法
1.3、Lambda表达式练习
1.3.1、无参无返回值抽象方法的练习
//接口
public interface Eatable {
void eat();
}
//测试类
public class EatableDemo {
public static void main(String[] args) {
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("没事多吃点");
}
});
//Lambda表达式
useEatable(() -> {
System.out.println("没事多吃点");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
1.3.2、有参无返回值抽象方法的练习
//接口
public interface Flyable {
void fly(String s);
}
//测试类
public class FlyableDemo {
public static void main(String[] args) {
//在主方法中调用useFlyable方法
//匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
}
});
System.out.println("--------");
//Lambda
useFlyable((String s) -> {
System.out.println(s);
});
}
private static void useFlyable(Flyable f) {
f.fly("我要飞得更高");
}
}
1.3.3、有参有返回值抽象方法的练习
//接口
public interface Addable {
int add(int x,int y);
}
//测试类
public class AddableDemo {
public static void main(String[] args) {
//在主方法中调用useAddable方法
useAddable((int x,int y) -> {
return x + y;
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
1.6、Lambda表达式和匿名内部类的区别
-
所需类型不同:
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
-
使用限制不同:
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
-
实现原理不同:
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
2、函数式接口
2.1、概念
有且仅有一个抽象方法的接口就是函数式接口,并且可以通过在类上标注@FunctionalInterface注解进行检测。
2.2、内置接口(四大内置函数式接口)
2.2.1、Supplier接口:供给型接口(无参有返回值的接口)
public class LambdaDemo2 {
public static void main(String[] args) {
Supplier<Integer> supplier = () -> Math.max(10, 20);
Integer result = supplier.get();
System.out.println(result);//20
}
}
2.2.2、Consumer接口:消费型接口(有参无返回值的接口)
public class lambdaDemo3 {
public static void main(String[] args) {
//printStr(s -> System.out.println(s.toUpperCase()));
printStr(s -> System.out.println(s.toUpperCase()), s -> System.out.println(s.toLowerCase()));
}
public static void printStr(Consumer<String> consumer) {
consumer.accept("Hello,World");//HELLO,WORLD
}
public static void printStr(Consumer<String> consumer1, Consumer<String> consumer2) {
consumer1.andThen(consumer2).accept("Hello,World");
//HELLO,WORLD
//hello,world
}
}
两个参数的可以参考BiConsumer类
2.2.3、Function接口:函数型接口(有参有返回值的接口,第一个参数为输入值,第二个参数为输出值)
public class LambdaDemo4 {
public static void main(String[] args) {
printNum(s -> Integer.parseInt(s));
Function<Integer, Integer> times2 = i -> i * 2;
Function<Integer, Integer> squared = i -> i * i;
//32,先执行apply(4),再4×4,然后16×2
System.out.println(times2.compose(squared).apply(4));
//64,先执行apply(4),再4×2,然后8×8
System.out.println(times2.andThen(squared).apply(4));
}
public static void printNum(Function<String, Integer> function) {
Integer result = function.apply("10");
System.out.println(result);//10
}
}
两个输入值可参考BiFunction类
2.2.4、Predicate接口:断定式接口(用于条件判断的场景)
public class LambdaDemo5 {
public static void main(String[] args) {
printResult(s -> s.contains("H"));
printAnd(s -> s.contains("H"), s -> s.contains("W"));
printOr(s -> s.contains("H"), s -> s.contains("W"));
printNegate(s -> s.contains("H"));
}
public static void printResult(Predicate<String> predicate) {
boolean result = predicate.test("Hello,World");
System.out.println(result);//true
}
public static void printAnd(Predicate<String> predicate1, Predicate<String> predicate2) {
boolean result = predicate1.and(predicate2).test("Hello,World");
System.out.println(result);//true
}
public static void printOr(Predicate<String> predicate1, Predicate<String> predicate2) {
boolean result = predicate1.or(predicate2).test("Hello,World");
System.out.println(result);//true
}
public static void printNegate(Predicate<String> predicate) {
boolean result = predicate.negate().test("Hello,World");
System.out.println(result);//false
}
}
3、方法引用
3.1、相关概念
符号表示:::
符号描述:双冒号为方法引用运算符,而他所在的表达式被称为方法引用。
应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。
3.2、常见形式:
-
对象::方法名Date date = new Date(); Supplier<Long> supplier = date::getTime; Long time = supplier.get(); System.out.println(time); -
类名::静态方法Supplier<Long> supplier = System::currentTimeMillis; Long time = supplier.get(); System.out.println(time); -
类名::普通方法Function<String, Integer> function = String::length; Integer length = function.apply("Hello"); System.out.println(length); -
类名::new 调用对象的构造器Function<String, String> function = String::new; String result = function.apply("Hello"); System.out.println(result); -
数据类型[]::new 调用数组的构造器Function<Integer, int[]> function = int[]::new; int[] result = function.apply(10); System.out.println(Arrays.toString(result));