change of interface

在java8中,interface可加入一个由default关键字修饰的method,这个method类似于abstract method,有具体的实现方式,但却不需要subclass继承

interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

why we need lambda expressions

lambda expressions的出现最主要的目的就是为了简化anonymous class的书写 ,上述代码可做如下改写

Formula formula1 = a -> sqrt(a * 100);

however,如果真的在compiler上做这样的改写,你会发现这会出现compile error:stuck_out_tongue_winking_eye:,实际上这里涉及到一个lambda的scope问题,这在后面会有详细地讲解

FunctionalInterface annotation

@FunctionalInterface这个注解是解决interface中type inference问题。当interface中含有generic type时,需要加入此注解来让compiler识别出确定的type,如以下代码。

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

lambda in constructor and method

constructor和method的lambda expressions是使用运算符::。这本质上就是用constructor或method来替换对应在interface中declare的method。因为是替换,所以parameter type和return type都必须一致。而对于method来说,需要使用static来修饰,否则需要相关的object。

//constructor相关
class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

PersonFactory<Person> personFactory = Person::new;//这里使用的是第二个constructor
Person person = personFactory.create("Peter", "Parker");
//method相关
class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}

Something something = new Something();
Converter<String, String> converter = something::startsWith;//没用static修饰
String converted = converter.convert("Java");
System.out.println(converted);    // "J"

lambda scopes

这里借官方文档的一段话:

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

大意就是关键字names、this和super,以及the accessibility of referenced declarations都与周围的上下文一样,也就是所有variable和method的reference都是与周围上下文一样的,新的variable和method需要做重新的declare。下面一段摘抄自StackOverflow中的code,可以很好地表达这其中的含义。

class Main {

    public static void main(String[] args) {
        new Main().foo();
    }

    void foo() {
        System.out.println(this);
        Runnable r = () -> {
            System.out.println(this);
        };
        r.run();
    }
}

输出的结果:

Main@f6f4d33
Main@f6f4d33

对于local variables,可以不使用final来做修饰,但必须是implicitly final,否则会有compile error。而对于field和static variables,都是和anonymous class一致,这里就不再赘述了

references: