目录
13.1、Lambda表达式
13.1.1、简介
Lambda 表达式(Lambda expression)是一个匿名函数,基于数学中的λ演算得名,也可称为闭包(Closure)。java、Python、JavaScript等等都支持 Lambda 表达式。
它是Java8的一个重要特性,我们可以把Lambda表达式当成是为是一段可以传递的代码,使用它能使代码更加简洁。
(参数列表) -> { // Lambda表达式体
}
->:Lambda 操作符
->的左侧代码:Lambda 表达式所需的参数列表。(一个参数无需定义圆括号,但多个参数需要定义圆括号)
->的右侧代码:Lambda 表达式中所需执行的功能,用{ }包起来,即 Lambda 体。(如果只包含了一个语句,可以不写大括号。)
这里有一个注解:@FunctionalInterface
,通过 @FunctionalInterface
标记的接口可以通过 Lambda 表达式创建实例(也就是函数式接口)
![image-20230219195533600](https://yudejava.oss-cn-hangzhou.aliyuncs.com/typora/202302191955670.png)
比如我们用过的Runnable 接口,它就 有@FunctionalInterface
注解
![image-20230219195723694](https://yudejava.oss-cn-hangzhou.aliyuncs.com/typora/202302191957791.png)
13.1.2、简单使用
我们可以使用Runnable来测试一下使用Lambda的效果
-
不使用
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("没有使用lambda"); } }).start(); }
-
使用Lambda表达式
public static void main(String[] args) { new Thread(() -> { System.out.println("使用了lambda"); }).start(); //还可以进一步省略 new Thread(() -> System.out.println("使用了lambda")).start(); }
可以明显看到使用 lambda表达式后,代码明显简洁了很多。
lambda表达式的几种写法:
-
()中不需要声明参数的类型,JVM会进行自动识别。
-
如果仅有一个参数时圆括号可以不写,但是无参数或多个参数需要定义圆括号。
-
如果Lambda表达式体 只有一个语句可以直接省略大括号
-
如果主体中仅仅只有一个返回语句,则大括号和关键字return都可以省略。
//无参,无返回值 Runnable runnable2 = () -> { System.out.println("无参数无返回值"); }; Runnable runnable1 = () -> System.out.println("无参数无返回值可省略{"); //一个参数,无返回值 Consumer<String> c1 = (String s) -> { System.out.println("格式写全"); }; Consumer<String> c2 = (s) -> { System.out.println("可以省略数据类型"); }; Consumer<String> c3 = s -> System.out.println("数据类型、()和{}都可以省略"); //两个以上参数,多条执行语句,返回值 Comparator<Integer> comparator = (x, y) -> { System.out.println("多参数,()不能省略,数据类型可省略"); System.out.println("多条句,{}不能省略"); return Integer.compare(x,y); }; //只有一个返回语句 Function<String,String> f1 = num -> "只有一个返回语句return和{}都能省略";
13.1.3、函数式接口
Lambda 表达式可以代替匿名内部类使用,使代码更加简洁。但不是所有的匿名内部类都可以简化为Lambda表达式,只有声明为函数式接口的匿名内部类才可以使用Lambda表达式来进行简化。
函数式接口:即接口中只有一个抽象的方法
。如果接口内实现了多个方法,使用 Lambda 表达式会出错。可以使用注解@FunctionalInterface声明函数式接口
,接口只要添加了该注解,就不可以再增加新的方法了。
//声明一个函数式的接口
@FunctionalInterface
public interface FunctionalInterfaceTest {
String showMessage(String name,String message);
}
public static void main(String[] args) {
//使用匿名内部类
FunctionalInterfaceTest functionalInterface1 = new FunctionalInterfaceTest() {
@Override
public String showMessage(String name, String message) {
System.out.println(name+"传达消息:"+message);
return "传达成功";
}
};
String s1 = functionalInterface1.showMessage("匿名内部类", "实现函数式接口");
System.out.println(s1);
//Lambda 表达式代替匿名内部类
FunctionalInterfaceTest functionalInterface2 =(name,message) -> {
System.out.println(name+"传达消息:"+message);
return "传达成功";
};
String s2 = functionalInterface2.showMessage("Lambda", "实现函数式接口");
System.out.println(s2);
}
![image-20220909112656851](https://yudejava.oss-cn-hangzhou.aliyuncs.com/typora/202301202311533.png)
虽然 Lambda使代码简洁,方便函数式编程,容易进行并行计算。但是它也容易导致代码的可读性变差。
13.1.4、当成参数传递
如果我们在方法中传递的参数是函数式接口的,那么我们可以直接使用 Lambda 表达式作为参数传递进去
public static void main(String[] args) {
test((name,message) -> {
System.out.println(name+"传达消息:"+message);
return "";
},"aaa","bbb");
}
public static void test(FunctionalInterfaceTest interfaceTest,String name ,String message){
interfaceTest.showMessage(name,message);
}
13.1.5、内置函数接口
在JDK中内置了四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 方法 | 用途 |
---|---|---|---|---|
Consumer消费型接口 | T | void | void accept(T t) | 只能接受一个输入参数并且无返回值 |
Supplier 供给型接口 | 无 | T | T get(); | 返回类型为T的对象 |
Function<T, R>函数型接口 | T | R | R apply(T t) | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象 |
Predicate断言型接口 | T | boolean | boolean test(T t) | 确定类型为T的对象是否满足某约束,并返回boolean |
@Test
public void mainTest(){
//消费型接口。无返回值,有参数
Consumer<String> consumer = (name)->{
System.out.println("===测试consumer===");
System.out.println(name);
};
consumer.accept("张三");
//供给型接口,无参数有返回值
Supplier<String> supplier = () -> {
System.out.println("===测试supplier===");
return "supplier的返回值";
};
String s = supplier.get();
System.out.println(s);
//函数型接口,有参数有返回值
Function<String, String> function = (name)-> {
System.out.println("===测试function===");
return "返回值是:"+name;
};
String apply = function.apply("张三");
System.out.println(apply);
//断言型接口
Predicate<String> predicate = (name) ->{
System.out.println("===测试predicate===");
return "张三".equals(name);
};
boolean b = predicate.test("张三");
System.out.println(b);
}
DK 1.8 之前已有的函数式接口:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
java.util.function:它内部包含了众多的接口
序号 接口 & 描述 1 四大内置接口 2 **BiConsumer<T,U>**代表了一个接受两个输入参数的操作,并且不返回任何结果 3 **BiFunction<T,U,R>**代表了一个接受两个输入参数的方法,并且返回一个结果 4 **BinaryOperator**代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 5 BiPredicate<T,U> 代表了一个两个参数的boolean值方法 6 BooleanSupplier代表了boolean值结果的提供方 7 DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 8 DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。 9 **DoubleFunction**代表接受一个double值参数的方法,并且返回结果 10 DoublePredicate代表一个拥有double值参数的boolean值方法 11 DoubleSupplier代表一个double值结构的提供方 12 DoubleToIntFunction接受一个double类型输入,返回一个int类型结果。 13 DoubleToLongFunction接受一个double类型输入,返回一个long类型结果 14 DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。 15 IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。 16 IntConsumer接受一个int类型的输入参数,无返回值 。 17 **IntFunction**接受一个int类型输入参数,返回一个结果 。 18 IntPredicate:接受一个int输入参数,返回一个布尔值的结果。 19 IntSupplier无参数,返回一个int类型结果。 20 IntToDoubleFunction接受一个int类型输入,返回一个double类型结果 。 21 IntToLongFunction接受一个int类型输入,返回一个long类型结果。 22 IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。 23 LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。 24 LongConsumer接受一个long类型的输入参数,无返回值。 25 **LongFunction**接受一个long类型输入参数,返回一个结果。 26 LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。 27 LongSupplier无参数,返回一个结果long类型的值。 28 LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。 29 LongToIntFunction接受一个long类型输入,返回一个int类型结果。 30 LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。 31 **ObjDoubleConsumer**接受一个object类型和一个double类型的输入参数,无返回值。 32 **ObjIntConsumer**接受一个object类型和一个int类型的输入参数,无返回值。 33 **ObjLongConsumer**接受一个object类型和一个long类型的输入参数,无返回值。 34 **ToDoubleBiFunction<T,U>**接受两个输入参数,返回一个double类型结果 35 **ToDoubleFunction**接受一个输入参数,返回一个double类型结果 36 **ToIntBiFunction<T,U>**接受两个输入参数,返回一个int类型结果。 37 **ToIntFunction**接受一个输入参数,返回一个int类型结果。 38 **ToLongBiFunction<T,U>**接受两个输入参数,返回一个long类型结果。 39 **ToLongFunction**接受一个输入参数,返回一个long类型结果。 40 **UnaryOperator**接受一个参数为类型T,返回值类型也为T。
13.2、方法的引用
方法引用通过方法的名字来指向一个方法,它的符号是 ::
两个冒号,一般配合Lambda表示一起使用,使用代码更加简洁。
1、方法引用
当要传递给Lambda体的操作,在lambad体中的内容有方法已经实现了,这时候就可以使用方法引用(实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!),它有三种格式:
-
对象 :: 实例方法名称
//消费型接口。不使用方法引用 Consumer<String> consumer = (name)->{ System.out.println(name); }; consumer.accept("张三"); //使用方法引用 Consumer<String> consumer1 = System.out::println; consumer.accept("张三");
-
类名称 :: 静态方法名称
Comparator<Integer> com1 = (x, y)->Integer.compare(x, y); System.out.println(com1.compare(2,2)); //使用方法引用 Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(2,2));
-
类名称 :: 实例方法名称
//未使用方法引用 BiPredicate<String,String> bp1 = (x, y)->x.equals(y); System.out.println(bp1.test("张三","里斯")); //使用方法引用 BiPredicate<String,String> bp2 = String::equals; System.out.println(bp2.test("张三","张三"));
在 jdk1.8 后 ,新增了很多可以用lambda表达式代替的方法,比如:Iterable中增加 forEach方法、Arrays操作数组中的排序方法void sort(T[] a, Comparator<? super T> c)等等,都可以把 Lambda 表达式当成参数进行传递。
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
![]()
2、构造器引用
属于方法引用的一种变体,格式:ClassName::new 或者 ClassName<>::new
![image-20230220150838945](https://yudejava.oss-cn-hangzhou.aliyuncs.com/typora/202302201508009.png)
Supplier<Persion> s1 = () -> new Persion("张三");
System.out.println(s1.get().getName());
Supplier<Persion> s2 = Persion::new;
System.out.println(s2.get().getName());
3、数组引用
格式:type[] :: new
Function<Integer,Integer[]> f1 = (n) -> new Integer[n];
Integer[] apply1 = f1.apply(5);
System.out.println(Arrays.toString(apply1));
System.out.println(apply1.length);
Function<Integer,Integer[]> f2= Integer[]::new;
Integer[] apply2 = f2.apply(5);
System.out.println(Arrays.toString(apply2));
System.out.println(apply2.length);