新特性
jdk 1.8 的大部新功能都是在方法可以被定义成变量的基础上扩展的(更基础的可能是方法的参数可以是代码片段)。
接口的默认方法
我们可以给接口添加一个或多个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做 扩展方法 ,可以参考 Comparator
接口,示例如下:
| interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
|
Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。
| Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0
|
接口的静态方法
在接口中,还允许定义静态的方法。接口中的静态方法可以直接用接口来调用。
| 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 的接口,只允许有一个抽象方法,否则编译器也会报错。
| @FunctionalInterface
public interface Runnable {
public abstract void run();
}
|
每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
| @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中是如何排列字符串的:
| 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表达式:
| 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表达式中访问外部变量
- 可以访问外部作用域(方法内部)的 final 类型的变量。
- 可以访问外部作用域(方法内部)的 普通变量,在Lambda中不可以修改该变量,如果在Lambad表达式外面有修改该普通变量的地方,则不能在Lambda表达式中引用该变量。
- 引用类的静态变量和非静态变量,都可以进行修改。
方法和构造引用
方法引用:
函数式接口:
| @FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
|
lambda实现接口的方法:
| Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
|
引用类的静态方法:
| Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
|
引用非静态方法:
Class:
| class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
|
引用:
| Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J"
|
构造引用:
Person类
| class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
|
Person工厂类
| interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
|
引用:
| PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
|
通过 Person::new
创建一个构造器的引用,编译器会根据 personFactory.create
的参数自动匹配对应的构造函数。相当于把构造函数赋值给 personFactory.create
。