java8 lambda表达式(2)
继续lambda表达式,结合简短的代码,详细深入说明lambda表达式。有兴趣也可以先读前一篇的内容。
1. 接口缺省方法
java8支持在接口中增加非抽象方法,也称为扩展方法。使用default关键字。示例如下:
interface Formula{
double calculate(int a);
defaultdouble sqrt(int a) {
return Math.sqrt(a);
}
}
Formula接口中定义了抽象方法calculate方法和default扩展方法。接口具体实现类只需要实现抽象方法,缺省方法sqrt可以直接使用。
Formulaformula=newFormula(){
@Override
publicdoublecalculate(inta){
returnsqrt(a*100);
}
};
formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0
formula赋值为一个匿名对象。代码冗长,6行代码,就为了调用calculate方法,后面利用java8的漂亮的方式简洁实现。
2. Lambda 表达式
首先,我们看看采用java8之前版本实现简单示例:
List<String>names=Arrays.asList("peter","anna","mike","xenia");
Collections.sort(names,newComparator<String>(){
@Override
publicintcompare(Stringa,Stringb){
returnb.compareTo(a);
}
});
static工具方法Collections.sort接收list和比较器,给list集合中所有元素进行排序。上面代码再次使用匿名比较器对象,传递给sort方法。
无需总是使用匿名对象,java8使用更简洁的语法实现,即lambda表达式。
Collections.sort(names, (Stringa, String b) -> {
return b.compareTo(a);
});
还可以更简洁,表达式只有一句,可以省略return关键字以及花括号。
Collections.sort(names, (Stringa, String b) -> b.compareTo(a));
再简洁点,数据类型也可以省略,java编译器可以推到其数据类型。
Collections.sort(names, (a,b) -> b.compareTo(a));
下面让我们更深入了解lambda表达式,实际情况下如何应用。
3. 函数式接口
lambda表达式如何符合java相关规范?每个lambda表达式与指定的接口、给定类型保持一致。所以也成为函数式接口,必须包含恰好只有一个抽象方法的申明,每个lambda表达式类型需和抽象方法匹配。因为缺省方法不是抽象的,所有你可以自由地增加缺省方法至函数式接口中。
我们可以使用任意接口做为lambda表达式,只要该接口只包含一个抽象方法。为了保证接口符合该需求,你可以增加@FunctionalInterface
注解进行限制。编译器根据该注解进行检查,当你试图增第二个抽象方法时,则会编译出错。示例如下:
@FunctionalInterface
interface Converter<F,T>{
Tconvert(Ffrom);
}
Converter<String,Integer> converter=(from)->Integer.valueOf(from);
Integer converted=converter.convert("123");
System.out.println(converted); // 123
需要说明下,上面的代码,不写注解也是可以的。
4. 引用方法和构造函数
lambda表达式,还可以引用方法和构造函数。上面的示例,也可以简单通过引用static方法实现。示例如下:
<pre name="code" class="java">Converter<String,Integer> converter=Integer::valueOf;
Integer converted=converter.convert("123");
System.out.println(converted); // 123
java8支持通过::关键字引用方法或构造函数,上面示例展示如何引用static方法,那如何引用对象方法呢?看下面示例,Converter函数式接口前面已经定义,这里引用对象something的方法startsWith。方便吧,感慨下!
class Something{
StringstartsWith(Strings){
returnString.valueOf(s.charAt(0));
}
}
Something something=newSomething();
Converter<String,String> converter=something::startsWith;
String converted=converter.convert("Java");
System.out.println(converted); // "J"
让我们再看看::关键字如何引用构造函数,首先定义示例bean,带有两个构造函数。
class Person{
StringfirstName;
StringlastName;
Person(){}
Person(StringfirstName,StringlastName){
this.firstName=firstName;
this.lastName=lastName;
}
}
下面定义person工厂接口,用于创建person对象。
interface PersonFactory<PextendsPerson>{
Pcreate(StringfirstName,StringlastName);
}
代替手工实现工厂接口,我们使用::关键字引用构造器。当调用PersonFactory.create时,java编译器自动选择适合的构造函数。
PersonFactory<Person> personFactory=Person::new;
Person person=personFactory.create("Peter","Parker");
再提醒下,一定要有一个函数式接口哦,是不是觉得这个东东有点麻烦,还要自己定义,后面详述java8给我们准备啥了,这里暂且不表。
5. Lambda 表达式作用域
lambda表达式访问外部变量有非常类似匿名对象。可以方法局部的fianl变量,也可以方法属性和static变量。
5.1. 访问局部变量
我们可以在lambda表达式里访问外部的局部变量。示例如下:
final int num=1;
Converter<Integer,String> stringConverter = (from)->String.valueOf(from+num);
stringConverter.convert(2); // 3
与匿名对象的访问方式不同,这里可以不使用final关键字申明num局部变量,所以代码也可以这样。
但是,num变量其实编译时隐含为final类型,所以下面代码,不能编译通过。
int num=1; Converter<Integer,String> stringConverter = (from)->String.valueOf(from+num); stringConverter.convert(2); // 3 <strong><span style="color:#A9B7C6;background:#2B2B2B"></span></strong>
在lambda表达式里面修改也是禁止的,记住,其实就是final类型。
5.2. 访问属性及静态变量
与局部变量相比,我们完全可以在lambda表达式里面访问或修改实例的属性和静态变量,这与我们熟悉的匿名对象访问方式一致。
classLambda4{
static int outerStaticNum;
int outerNum;
voidtestScopes(){
Converter<Integer,String> stringConverter1=(from)->{
outerNum=23;
returnString.valueOf(from);
};
Converter<Integer,String> stringConverter2=(from)->{
outerStaticNum=72;
returnString.valueOf(from);
};
}
}
5.3. 访问缺省接口方法
还记得前面的Formula接口示例吗?
接口 Formula 定义了缺省的sqrt 方法,其可以被每个Formula实例对象访问,包括匿名对象,但是不能使用lambda表达式访问。缺省方法不能被lambda表达式访问,所以下面的代码不能编译。
Formulaformula=(a)->sqrt(a*100);
本文参考链接:https://blog.csdn.net/neweastsun/article/details/52902387