简述Java的Lambda表达式

https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

被Lambda表达式、方法引用(method reference)、Supplier接口这几个概念弄得有点晕,参考上面文档,简单梳理一下。

粗略讲:Lambda表达式帮你将函数看作一个参数传入方法中;方法引用是其中一种特殊情况;Supplier是java.util.function包众多接口一种,为前两者提供工具。

一、Lambda表达式

1.引入

以代码举例:有个社交网络应用,需要设计一个功能:用户选择范围,然后打印出满足条件的联系人。下面是联系人Person类:

public class Person {//联系人

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

使用匿名类

常用的一种方法是匿名类,实现功能的方法是printPersons(),因为范围可能会经常变,所以抽出来做一个接口CheckPerson,不同的范围对应不同实现:

interface CheckPerson {//范围接口
    boolean test(Person p);
}

public static void printPersons(//打印出满足条件的联系人
    List<Person> list, CheckPerson tester) {
    for (Person p : list) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

printPersons(//具体实现,范围是18-25岁的男性
    list,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

这样很灵活,范围的变化不会影响到printPersons();缺点是代码有点臃肿。

使用Lambda表达式

CheckPerson接口只有一个抽象方法test,我们把这样只有一个抽象方法(可以有一般方法和静态方法)的接口称作函数接口(functional interface。我们可以使用Lambda表达式来省略方法名称,让代码更简洁:

printPersons(
    list,
    (Person p) -> p.getGender() == Person.Sex.MALE //将匿名类替换成Lambda表达式
        && p.getAge() >= 18
        && p.getAge() <= 25
);

其实还可以再简洁,java提供了一套标准接口 java.util.function,替代自定义的CheckPersons。至于选择哪一个,则根据Lambda表达式的参数和返回值,例如上面只有一个Object类型参数p,返回值是boolean类型,所以匹配Predicate<T>,替换后代码更紧凑:

import java.util.function.Predicate;

public static void printPersons(
    List<Person> list, Predicate<Person> tester) {//使用标准接口Predicate替换自定义CheckPerson
    for (Person p : list) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

printPersons(
    list,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

2.语法

Lambda表达式包括三部分:

  1. 参数:由逗号分隔,包含在括号()里,可以省略类型。如果只有一个参数 也可以省略括号,例如上面的 p。
  2. 箭头 ->
  3. 主体:可以是表达式(expression),如上面的例子,Java runtime会识别并帮你返回值。也可以是大括号{ }包围的代码块(statement block),上面例子写成这种形式:
    p -> { //一旦使用return,必须用大括号包住
        return p.getGender() == Person.Sex.MALE 
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }

     

下面是两个参数的例子:

import java.util.funtion.IntBinaryOperator;//接受两个int型参数,返回int型结果

public class Calculator {

  
    public int operate(int a, int b, IntBinaryOperator op) {
        return op.applyAsInt(a, b);
    }
 
    public static void main(String... args) {
    
        Calculator myApp = new Calculator();
        //两个参数
        IntBinaryOperator addition = (a, b) -> a + b;
        IntBinaryOperator subtraction = (a, b) -> a - b;
        
        System.out.println("1 + 2 = " + myApp.operate(1, 2, addition));
        System.out.println("2 - 1 = " + myApp.operate(2, 1, subtraction));    
    }
}

3.使用时机和范围

时机:除了上面函数接口的情况,当你想将一段代码(a single unit of behavior)传给其他代码时,可以用Lambda对其概括。

范围:Lambda表达式作为参数,它的类型是什么?其实是根据上下文(context,situation)来决定的:当Java runtime调用 printPersons( List<Person> list, CheckPerson tester) 时,期望是CheckPerson类型,所以这个Lambda表达式就是CheckPerson类型,而调用 printPersons( List<Person> list, Predicate<Person> tester) 时,则Predicate<Person>类型,方法所期待的类型叫目标类型(target type, java.util.function就是为Lambda提供目标类型的

所以Lambda表达式只能在有目标类型的情况下使用:

  1. Variable declarations  变量声明

  2. Assignments  赋值操作

  3. Return statements   返回值return

  4. Array initializers   数组初始化

  5. Method or constructor arguments  方法/构造器参数

  6. Lambda expression bodies   Lambda表达式主体?

  7. Conditional expressions, ?: 条件表达式

  8. Cast expressions 类型转换

二、方法引用(method reference)

当Lambda表达式引用一个已经存在的方法,可以用双冒号::直接引用方法名,分为四种情况:

  • 静态方法:ClassName::staticMethodName
  • 一般方法:ObjectName::methodName
  • 任意对象的方法:ClassName::methodName
  • 构造方法: ClassName::new

第三种情况不太好懂,文档用的例子是:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

我的理解,数组中的字符串对象没有名称,无法用ObjectName直接引用,所以用ClassName。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值