Java Lambda 学习笔记

开发环境
  • eclipse 4.7.3a
  • jdk 9

案例

通常我们在开发过程中会遇到如下需求:

需求1:找到大于指定年龄的用户,并输出他们的个人信息

public static void printPersonsOlderThan(List roster, int age) {
    for (Person p : roster) {
        if (p.getAge() >= age) {
            p.printPerson();
        }
    }
}

需求2:找到指定指定年龄段的用户,并输出他们的个人信息

public static void printPersonsWithinAgeRange(
    List roster, int low, int high) {
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}

通过观察发现,我们可以定义一个抽象接口,返回符合特定信息的用户信息:

interface CheckPerson {
    boolean test(Person p);
}

将上述方法进行重构:

// 传入CheckPerson 来返回特定信息的用户
printPersons(
    List roster,
   CheckPerson checkPerson
);

//

在调用时可以通过匿名内部类方式实例化接口(老司机通常会这样 ~ ~)

// 找到指定指定年龄段的用户
printPersons(
    List roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

使用匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能看起来过于繁琐,还有没有更简洁的写法呢?

关于Lambada表达式

Lambda 是一个匿名函数,即没有函数名的函数 [Lambda表达式]。 Java 8 引入了 Lambda 表达式, 它允许把函数作为一个方法的参数,使用 Lambda 表达式使代码看起来更加"简洁紧凑"(有待考证 ~ ~)。

使用Lambda表达式调用printPersons方法:

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

我们发现匿名内部类被替换成了如下格式:

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

它还可以这样写:

(Person p) ->{ p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25;}

类和方法的声明被省略,只保留了方法的入参


(Person p) // 方法的入参

-> // 箭头的右边是方法的具体实现
访问局部变量

Lambda 可以访问局部变量,但不能访问局部变量表达式,否则会导致编译异常

        int x = 0;
        
        // The following statement causes the compiler to generate
        // the error "local variables referenced from a lambda expression
        // must be final or effectively final" in statement A:
        //
        // x = 99;
        
        Consumer myConsumer = (y) ->
        {
            System.out.println("x = " + x); // Statement A
            System.out.println("y = " + y);
            System.out.println("this.x = " + x);
            System.out.println("LambdaTest.this.x = " + demo.field0);
        };
Lambda的返回类型和方法参数

我们来分别研究Runnable接口和Callable接口,其中Runnable 接口没有入参也不需要返回值,Callable接口也不需要入参,返回指定泛型类型。

Tips: Runnable 和 Callable都用于线程实现,Callable可以阻塞等待线程返回结果。

public interface Runnable {
    void run();
}

public interface Callable {
    V call();
}

对应的lambda表达式如下:

Runnable  r = () ->{ System.out.println("haha");};

Callable c = () ->{ return "done";};
总结

要确定lambda表达式的类型,Java编译器将使用发现lambda表达式的上下文或情境的目标类型。因此只能在Java编译器可以确定目标类型的情况下使用lambda表达式:

  • 变量声明
  • 分配
  • 返回值
  • 数组初始化器
  • 方法或构造函数参数
  • Lambda表达体
  • 三元条件表达式
  • 类型转换表达式

下一篇:方法引用

Github工程地址

你可能感兴趣的:(Java Lambda 学习笔记)