带你学会Lambda

220 阅读3分钟

前言

public interface Student {
    void study(String subject);
    void run();
}

@Test
public void test() {
    Student student = new Student() {
        @Override
        public void study(String subject) {
            System.out.println("学习" + subject);
        }

        @Override
        public void run() {
            System.out.println("跑步");
        }
    };

    student.study("英语");
    student.run();
}

你可能不了解Lambda,但是你一定见过上面的写法(匿名内部类)。那么Java在一定程度上,通过Lambda对于这种匿名内部类进行了优化。但是,Lambda只针对于函数式接口
函数式接口有一个最大的特点,只能有一个公共抽象类

// 伪函数式接口
public interface Student {
    void study(String subject);
}

@Test
public void test() {
    Student student = new Student() {
        @Override
        public void study(String subject) {
            System.out.println("学习" + subject);
        }
    };
    student.study("英语");
    
    System.out.println("********下面通过Lambda改造********");
    
    Student stu = (subject) -> System.out.println("学习" + subject);
    stu.study("语文");
}

Java提供一个注解@FunctionalInterface标记该接口是函数式接口强制规定接口中仅有一个公共抽象类

@FunctionalInterface
public interface Student {
    // 有两个公共抽象类,则会编译异常:
    // Multiple non-overriding abstract methods found in interface com.shang.lambda.Student
    void study(String subject);
    void run();
}

函数式接口

Java内置四种基本的函数式接口,如下。
1. 消费型接口( Consumer<T> ) : void accept(T t)
2. 供给型接口(Supplier<T>) :T get()
3. 函数型接口(Function<T, R>) : R apply(T t)
4. 断言型接口(Predicate<T>) : boolean test(T t)

@Test
public void test2() {
    consumer(500.00, (money) -> System.out.println("赵公子消费" + money + "元"));
}
public void consumer(Double money, Consumer<Double> c) {
    c.accept(money);
}

在上面的示例中,(money) -> System.out.println("赵公子消费" + money + "元")有一个明显的特征,传入一个值,没有返回值,显然它符合消费型接口( Consumer<T> ),所以可以传一个函数体给它。其它函数式接口类似。

语法糖

上面都是通过Lambda实现抽象类,自定义实现体。那么对于已经实现的方法,我们只需要直接使用,而不是实现。恰好这些已经实现的方法符合函数式接口的形式,我们就可以通过语法糖直接调用。

方法引用构造器引用
对象::实例方法名类::new
类::静态方法名类[]::new
类::实例方法名
    // 1. 对象::实例方法
    Consumer<String> com = str -> System.out.println(str);
    // 等效于 Consumer<String> com = System.out::println;
    com.accept("北京");
    
    // 2. 类::静态方法
    Comparator<Integer> com = (t1,t2) -> t1.compareTo(t2);
    // 等效于 Comparator<Integer> com = Integer::compareTo;
    com.compare(12,20);
    
    // 3. 类::实例方法
    Comparator<String> com = (s1,s2) -> s1.compareTo(s2);
    // 等效于 Comparator<String> com = String::compareTo;
    com.compare("abc","bcd");
    
    
    Function<Teacher, String> fun = t -> t.getName;
    // 等效于 Function<Teacher, String> fun = Teacher::getName;
    fun.apply(teacher)
    
    // 4. 类::new
    Supplier<Teacher> sup = Teacher::new; // 无参构造器
    Teacher teacher = sup.get();
    
    Function<String, Teacher> sup1 = (name) -> new Teacher(name); 
    // 等效于 Function<String, Teacher> sup1 = Teacher::new // 1个参数构造器
    sup1.apply("张老师");
    
    BiFunction<String,Integer,Teacher> sup1 = (name,age) -> new Teacher(name,age); 
    // 等效于 BiFunction<String,Integer,Teacher> sup1 = Teacher::new // 2个参数构造器
    sup1.apply("张老师",30);

能够使用语法糖,需要满足两点要求第一点,方法已经被实现第二点:方法符合函数式接口特征

结尾

除了基本的四种函数式接口外,Java还有一些变种
1. BiFunction<T, U, R> : R apply(T t, U u)
2. UnaryOperator<T>:T apply(T t)
3. BinaryOperator<T>:T apply(T t1, T t2)
4. BiConsumcr(T, U):void accept(T t, U u)
5. ToIntFunction<T>、ToLongFunction<T>、ToDoubleFunction<T>:int applyAsInt(T value)、long applyAsLong(T value)、double applyAsDouble(T value)
6. IntFunction<R>、LongFunction<R>、DoubleFunction<R>:R apply(int value)、R apply(long value)、R apply(double value)