java 1.8之前,接口里面只能定义抽象方法,1.8之后再接口里面可以定义非抽象的方法了,主要使用default
关键字定义,如下:
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
java 1.8之前要实现集合里面的元素的排序一般都是写一个Comparator
接口的匿名实现类,如下:
List names = Arrays.asList("张三", "李四", "隔壁老王");
Collections.sort(names, new Comparator() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
上面这种写法太麻烦了,java 1.8中增加了一个更加简单的语法:lambda表达式来代替这种写法。
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
更加简单的可以这样写:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
甚至这样写:
names.sort((a, b) -> b.compareTo(a));
为什么可以这么写,往下看慢慢来分析。
就像我们以前所认知的一样,java是一种强类型匹配的语言,那么这种lambda表达式怎么能够和java的类型对应起来呢?所以java 1.8提出了@FunctionalInterface功能性接口的概念。功能性接口首先是一个接口,然后只能有一个自带的抽象方法(继承的除外)。每一个lambda表达式将会这个抽象方法相匹配。通过@FunctionalInterface注解告知编译器这是一个功能性接口(也可以不写,编译器自己会知道)
例如:
@FunctionalInterface
interface Converter {
T convert(F from);
}
Converter converter = (from) -> Integer.valueOf(from);//后面的这个lambda表达式,所对应的方法就是Converter接口里面的convert方法,其中“(from)”标示参数,“Integer.valueOf(from)”标示方法的返回值, “->”是语法规则
Integer converted = converter.convert("123");//在方法的调用的时候,参数的类型是通过泛型进行匹配的。
System.out.println(converted); // 123
上面的方法其实还可以通过静态方法引用再简化,java 1.8允许通过“::”关键字传递方法引用。
Converter converter = Integer::valueOf;
Integer converted = converter.convert("123");//对convert方法的调用直接调用Integer.valueOf(String)方法,并返回
System.out.println(converted); // 123
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Converter converter = something::startsWith;
String converted = converter.convert("Java");//对convert方法的调用直接调用something实例的,并返回startsWith(String s)方法.
System.out.println(converted); // "J"
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
interface PersonFactory {
P create(String firstName, String lastName);
}
PersonFactory personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");//调用 create方法,自动寻找对应的Person实例的构造方法,并返回
在lambda表达式里面访问外部变量和匿名对象十分的访问方式十分相似。
final int num = 1;
Converter stringConverter =
(from) -> String.valueOf(from + num);//访问局部变量,局部变量要定义为final类型
stringConverter.convert(2); // 3
如果局部变量不定义为final类型,只要在lambda里面没有改变这个变量的值,可以运行,如果改变了,编译过不了。
class Lambda4 {
static int outerStaticNum;
int outerNum;
void testScopes() {
Converter stringConverter1 = (from) -> {
outerNum = 23;
return String.valueOf(from);
};
Converter stringConverter2 = (from) -> {
outerStaticNum = 72;
return String.valueOf(from);
};
}
}
在lambda里面访问默认方法直接编译错误。
Formula formula = (a) -> sqrt( a * 100);//complie error
分析一下为什么可以这么写:方式用到接口泛型的地方,所有的参数类型,编译器会自动推断。lambda表达式里面的参数也是自动推断出来的。