一、Lambda 表达式
- Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)
- Lambda表达式,实际上还是一个接口的一个匿名实现类,但该接口只能有一个抽象方法
- 从匿名类到Lambda
@FunctionalInterface public interface MathCalculate<T> { int method(T a, T b); }@Test public void test2() { // 匿名内部类的方法 MathCalculate<Integer> mathCalculate = new MathCalculate<>() { @Override public int method(Integer a, Integer b) { return a + b; } }; int res = mathCalculate.method(1, 2); System.out.println(res); // 3 }@Test public void test() { // Lambda表达式的方法 MathCalculate<Integer> mathCalculate = (x, y) -> x + y; int res = mathCalculate.method(1, 2); System.out.println(res); } - Lambda 表达式语法:
- 左侧:指定了Lambda 表达式需要的所有参数(接口形参有什么,就写什么,不需要带类型)
- 右侧:指定了Lambda体,即Lambda表达式要执行的功能(其实就是接口实现的内容)
- 语法格式一:无参,无返回值,Lambda体只需要一条语句
@Test public void runnableTest() { Runnable runnable = () -> System.out.println("你好世界"); runnable.run(); } - 语法格式二:Lambda需要一个参数,无返回值,参数的括号可以省略
@Test public void consumerTest() { Consumer<String> talk = (str) -> System.out.println(str); //Consumer<String> talk = str -> System.out.println(str); talk.accept("你好世界"); } - 语法格式三:Lambda 需要两个参数,并且有返回值
@Test public void test() { // Lambda表达式的方法 MathCalculate<Integer> mathCalculate = (x, y) -> x + y; int res = mathCalculate.method(1, 2); System.out.println(res); } - 语法格式四:Lambda表达式有多条语句,需要用大括号括住
@Test public void runnableTest() { Runnable runnable = () -> { System.out.println("你好世界"); System.out.println("第二个你好世界"); }; runnable.run(); }
二、函数式接口
-
只包含一个抽象方法的接口,称为函数式接口
-
我们可以在任意函数式接口上使用
@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。@FunctionalInterface public interface MathCalculate<T> { int method(T a, T b); } -
作为参数传递Lambda表达式(其实就是接口作为形参,然后接收表达式)
public class RunnableUser { // 接受Runnable的接口实现类或者Lambda表达式作为参数注入 public void useRunnable(Runnable runnable) { runnable.run(); } }@Test public void runnableTest() { Runnable runnable = () -> { System.out.println("你好世界"); System.out.println("第二个你好世界"); }; RunnableUser runnableUser = new RunnableUser(); runnableUser.useRunnable(runnable); }@Test public void runnableTest() { RunnableUser runnableUser = new RunnableUser(); runnableUser.useRunnable(() -> System.out.println("你好世界")); } -
Java已经内置了一些核心函数式接口,因此一般来说都不需要自己创建,除非有特殊要求。
函数式接口 参数类型 返回类型 用途 Consumer<T>消费者接口Tvoid用于有一个输入参数,没有返回值;包含方法 accept(T t)Supplier<T>供给型接口无 T用于无输入参数,有一个返回值;包含方法 T get()Function<T, R>函数型接口TR用于有一个输入参数,一个返回值;包含方法 R apply(T t)Predicate<T>断定型接口Tboolean用于判断某个值是否满足约束,输入一个参数,返回 boolean类型;包含方法boolean test(T t)还有更多接口,可以查看以下链接:Java 8 函数式接口 -
例子:
@Test public void consumerTest() { Consumer<String> talk = (str) -> System.out.println(str); //Consumer<String> talk = str -> System.out.println(str); talk.accept("你好世界"); } @Test public void supplierTest() { Supplier<String> getSomething = () -> "你好世界"; String res = getSomething.get(); System.out.println(res); } @Test public void functionTest() { Function<String, Integer> Parse = (str) -> Integer.parseInt(str); Integer integer = Parse.apply("10"); System.out.println(integer); } @Test public void predicateTest() { Predicate<Integer> filter = (num) -> num >= 100; System.out.println(filter.test(100)); System.out.println(filter.test(77)); System.out.println(filter.test(110)); }
三、方法引用与构造器引用
- 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
- 实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致(包括输入参数、返回值)
- 使用操作符
::将方法名和对象或类的名字分隔开来 - 有三种使用情况:
对象::实例方法、类::静态方法:这两种方法很类似@Test public void functionTest() { Function<String, Integer> Parse = Integer::parseInt; // Integer的静态方法 Integer integer = Parse.apply("10"); System.out.println(integer); }类::实例方法:这种方法比较特别,相当于接口需要调用者传入一个对象及其参数。@Test public void compareTest() { BiPredicate<String, String> comparator = String::equals; // BiPredicate<String, String> comparator = (x, y) -> x.equals(y); // 等价于 boolean res = comparator.test("hello", "hello"); System.out.println(res); }
- 构造器引用
实际上构造器就是一个静态方法。因此,可以这样写:
@Test public void newTest() { Supplier<User> supplier = User::new; // 无参构造 User user = supplier.get(); System.out.println(user); Function<Integer,User> function = User::new; // 有参构造 user = function.apply(1); System.out.println(user); }使用哪个构造器,要看使用哪个函数式接口。
- 数组引用
@Test public void newArrayTest() { Function<Integer,Integer[]> function = Integer[]::new; Integer[] apply = function.apply(2); // 需要传进去一个数组长度 System.out.println(apply.length); // 2 System.out.println(Arrays.toString(apply)); // [null, null] }