Lambda运算符因其方便简洁等原因,早已成熟地被运用在C++、C#等编程语言中。作为一个重大更新,java 8终于引进这个已被广大开发者期待已久的新特性,本文初探这一特性,作引路之用。
- 为什么要用Lambda表达式
- 语法
- 适用范围
- 函数式接口
- 特点
- 使用
- 总结
为什么要用Lambda表达式
- 刚开始接触这玩意,也想了很久:为什么非要用这个语法,又挫又怪又丑?总是不愿正眼瞧它,后来想想,作为一名程序猿,总是这么故步自封,似乎不妥,就硬着头皮学了一下,现在看它的优点主要的大抵也就一个显要的:这货简化了匿名委托的使用。我们都知道,回调模式和函数式编程需要我们创建大量的类来实现接口,而这个类往往只使用一次作为内联,实例化一个类是一个比较重量级的行为,我们就需要一种轻量级的开销来传递我们自己的数据。匿名内部类的语法相对来说是繁琐了。说白了,就是支持将代码块作为方法参数,让那种只有一个抽象方法的接口创建实例的时候,方便快捷,读起来更潮更屌更优雅罢了……
语法
语法格式由3部分组成:形参列表;箭头;代码块,形如()->{}。
形参列表:一般会省略形参类型,本来就为简洁而生的嘛。如果形参列表只有一个参数,那就更方便,连括号直接也可丢了。
箭头:->,英文中划线号和大于号组成。
代码块:同形参列表一样,如果只有一条语句,花括号同样可以省略。需要说明的是,如果只有一条return语句,return关键字和花括号同样可以省略。
一句话:参数箭头大括号。
适用范围
创建函数式接口的实例
函数式接口
简单说,就是只包含一个抽象方法的接口。注意,一个函数式接口只能包含一个抽象方法,并不是说这种接口只能有一个方法,它可以包含多个默认方法、一些类方法,但是声明中有且只能有一个抽象方法。
java8提供了@FunctionalInterface注解来专门修饰这种接口。如果接口带了这种修饰符,编译器在编译的时候,就会严格检查是否符合函数式接口的标准。如Api中提供的Runnable、Callable、ActionListener等接口。
java8在java.util.function包下提供了大量函数式接口,命名上一般有如下特点:XxxFunction,XxxConsumer,XxxPredicate,XxxSupplier等
特点
前面说过,Lambda表达式主要是用来替代用函数式接口创建匿名内部类。当然,匿名内部类的存在,亦必有它的不可取代之处。两者的比较,某讲义上略总结了一些,这里就借鉴来了。
匿名内部类的使用对象可以是任何接口,但Lambda表达式的引用对象只能是函数式接口。匿名内部类在创建对象是,不会对接口类型有要求,只需实现接口内所有方法即可,Lambda只能为函数式借口创建实例;
匿名内部类可以为抽象类、普通类创建实例。Lambda就不如这个屌了,它的目标类型必须是明确的函数式接口,如果不是则考虑转型。
匿名内部类实现抽象方法的方法体可以调用接口中其他的默认方法,Lambda表达式的代码块就不允许。
两者的区别明显了吧,一个呢,就像一把三环钥匙,只能开一把三环锁;另一个呢,则是一把强化版的三环钥匙,逢锁便开。
使用
以下展示了两种使用方法,一个是简单的入门示例,另外一些是使用引用方法和构造器等更快捷的方法。
入门示例
public class LambdaTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
LambdaTest lambdaTest = new LambdaTest();
//方法体只有一句话,省略花括号
lambdaTest.oneTest(()->System.out.println("接口一的方法体"));
//形参只有一个,省略圆括号
lambdaTest.twoTest(name->{
System.out.println("接口二的方法体1"+name);
System.out.println("接口二的方法体2");
});
//方法体只有一句话,并省略return关键字
lambdaTest.threeTest((a,b)->a+b);
}
public void oneTest(One one) {
System.out.println(one);
one.oneMethod();
}
public void twoTest(Two two) {
System.out.println("第2个测试" + two);
two.twoMethod("你好");
}
public void threeTest(Three three) {
System.out.println("6+4的和是:" + three.threeMethod(6, 4));
}
}
interface One {
void oneMethod();
}
interface Two {
void twoMethod(String name);
}
interface Three {
int threeMethod(int a, int b);
}
来一个更直观的
Runnable runnable = ()->{
for(int i = 0 ;i<50;i++){
System.out.println(“”+i);
}
};
如果不小心将上面的代码写成这样还希望侥幸过关的话,
Object object = ()->{
for(int i = 0 ;i<50;i++){
System.out.println(“”+i);
}
};
聪明的编译器必然是不会放过你的。不如这样改一下:
Object object = (Runnable)()->{
for(int i = 0 ;i<50;i++){
System.out.println(“”+i);
}
};
或者
Object object = (MyInterface)()->{
for(int i = 0 ;i<50;i++){
System.out.println(“”+i);
}
};
@FunctionalInterface
interface MyInterface{
void run();
}
所以你会发现,Lambda表达式的目标类型完全可能是多个,唯一的限制是表达式实现的方法参数列表和接口中抽象方法的形参列表相同。
引用方法和构造器
- 引用类方法
@FunctionalInterface
interface Convert{
Integer convert(String from);
}
使用Lambda表达式创建Converter对象
Converter converter = from->Integer.valueOf(from);
使用方法引用(类方法)替代
Converter converter = Integer::valueOf;
规则:
类名::类方法,对应的Lambda表达式:(a,b,…)->类名.方法(a,b,…);
- 引用特定对象的实例方法
使用Lambda表达式创建Converter对象
Converter converter = from->”hahaitbb”.indexOf(from);
使用方法引用(String对象的indexof()方法)替代
Converter converter = “hahaitbb”::indexOf;
规则:
特定对象::实例方法,对应的Lambda表达式:(a,b,…)->特定对象.实例方法(a,b,…);
- 某类实例对象的特定方法
@FunnctionalInterface
Interface MyInterface{
String test(String name,int a,int b);
}
使用Lambda表达式创建MyInterface对象
MyInterface myInterface = (name,a,b)->name.substring(a,b);
使用方法引用(String类的substring())替代
MyInterface myInterface = String::sunstring;
规则:
类名::实例方法,对应的Lambda表达式:(a,b,….)->a.实例方法(b,…);
- 引用构造器
@FunnctionalInterface
interface HelloTest{
Person rebuild(String str)
}
public class Person{
Person(String name){
}
}
使用Lambda表达式创建HelloTest对象
HelloTest helloTest = (String a)->new Person(a);
引用构造器代替
HelloTest helloTest = Person::new;
规则:
类名::new,对应的Lambda表达式:(a,b,…)->new (a,b,…);
总结
初次接触Lambda表达式,可能会感觉有些奇怪,而且一时半会儿也觉不出来它的优势,有过c++或者.net等编程经验的可能会熟悉一些。但作为程序猿,吸纳新事物总是必须的,况且别的语言已经运用的相当成熟,作为世界上最优秀的程序语言(好吧…python怒了)自然不能落后了。本文可作为初涉者引导,高手绕行^_^