跳转至

新特性

jdk 1.8 的大部新功能都是在方法可以被定义成变量的基础上扩展的(更基础的可能是方法的参数可以是代码片段)。

接口的默认方法#

我们可以给接口添加一个或多个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做 扩展方法 ,可以参考 Comparator 接口,示例如下:

1
2
3
4
5
6
interface Formula {
    double calculate(int a);
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。

1
2
3
4
5
6
7
8
9
Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

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

接口的静态方法#

在接口中,还允许定义静态的方法。接口中的静态方法可以直接用接口来调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public interface StaticFunInterface {
    public static int find(){
        return 1;
    }
}
public class TestStaticFun {
    public static void main(String[] args){
        //接口中定义了静态方法 find 直接被调用
        StaticFunInterface.fine();
    }
}

函数式接口#

Java 8 引入的一个核心概念是函数式接口(Functional Interfaces)。通过在接口里面添加一个抽象方法,这些方法可以直接从接口中运行。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。同时,引入了一个新的注解:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。这个注解是非必须的,只要接口只包含一个方法的接口,虚拟机会自动判断,不过最好在接口上使用注解 @FunctionalInterface 进行声明。在接口中添加了 @FunctionalInterface 的接口,只允许有一个抽象方法,否则编译器也会报错。

1
2
3
4
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

1
2
3
4
5
6
7
@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 表达式#

Lambda 表达式(也叫做闭包)是Java 8中最大的也是期待已久的变化。它允许我们将一个函数当作方法的参数(传递函数),或者说把代码当作数据。
Lambda 表达式是在函数式接口的基础上实现的,Lambda 表达式的内容等价于一个简化的接口实现。

首先看看在老版本的Java中是如何排列字符串的:

1
2
3
4
5
6
7
8
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

只需要给静态方法 Collections.sort 传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。

在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

1
2
3
4
5
6
7
Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

Collections.sort(names, (String a, String b) -> b.compareTo(a));

Collections.sort(names, (a, b) -> b.compareTo(a));

在Lambda表达式中访问外部变量

  1. 可以访问外部作用域(方法内部)的 final 类型的变量。
  2. 可以访问外部作用域(方法内部)的 普通变量,在Lambda中不可以修改该变量,如果在Lambad表达式外面有修改该普通变量的地方,则不能在Lambda表达式中引用该变量。
  3. 引用类的静态变量和非静态变量,都可以进行修改。

方法和构造引用#

方法引用:
函数式接口:

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

lambda实现接口的方法:

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

引用类的静态方法:

1
2
3
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);   // 123

引用非静态方法:
Class:

1
2
3
4
5
class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}

引用:

1
2
3
4
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);    // "J"

构造引用:

Person类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Person {
    String firstName;
    String lastName;

    Person() {}

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

Person工厂类

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

引用:

1
2
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

通过 Person::new 创建一个构造器的引用,编译器会根据 personFactory.create 的参数自动匹配对应的构造函数。相当于把构造函数赋值给 personFactory.create