Java学习笔记(疯狂Java讲义第三版)——Lambda表达式

什么是Lambda表达式?
是一种创建实例的语法,这类语法要比匿名内部类创建实例时还要简洁。(个人理解)
Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口的实例。(书中原文)

在什么情况下,可以使用Lambda表达式?
我们先看下什么样的情况可以通过定义匿名内部类的形式来创建实例:
1、只继承一个类(普通类或抽象类)。
2、或者只实现一个接口。
例如:
class A
{
}

interface B
{
}

class Test
{
A a = new A(){}//定义一个继承A类的匿名内部类
B b = new B(){};//定义一个实现B类接口的匿名内部类
}

对于Lambda表达式来说,只能在创建实现函数式接口的实例时,可以通过Lambda表达式的方式来创建该实例。条件比匿名内部类要严格的多。
首先只能实现一个接口,且该接口只有一个抽象方法。

什么是函数式接口?
本质是一个接口,一个特殊的接口,该接口中只有一个抽象方法。——只有一个抽象方法的接口为函数式接口。

总结下:只有在创建一个实现函数式接口的实例时,才可以以Lambda表达式的方式来创建该实例。——即只能创建实现函数式接口的实例。

此时,问一个问题。在创建一个实现函数式接口的类实例时,可不可以使用匿名内部类的形式呢?

答案当然是可以的。看下匿名内部类的使用规则:在创建一个只继承一个类或者只实现一个接口实例时可以使用匿名内部类的形式创建该实例。函数式接口只是一个特殊的接口,所以针对创建一个实现了函数式接口的实例的时也是可以通过匿名内部类的形式来创建该实例的。

所以,Lambda表达式完全可以用于简化匿名内部类的形式创建实例。下面有详细演示。

问题:创建一个实现函数式接口的实例时,以匿名内部类的形式和Lambda表达式的形式有区别吗?

我个人的理解在使用语法上,Lambda就是比匿名内部类更简洁的语法形式(创建函数式接口实例时)。但是细则上有所差别。

先看Lambda就是比匿名内部类更简洁的语法形式(创建函数式接口实例时)
先看下Lambda表达式的语法规则:
主要由三部分组成:
1、形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,则形参列表的圆括号也可以省略
2、箭头(->)必须由英文中划线和大于号组成
3、代码块。如果Lambda表达式代码块中只有一条语句,则大括号可以省略。如果Lambda表达式代码块只有一条语句且这条语句是return语句则return关键字也可以省略。关于这一点,重点说明下。对于一个有返回值的方法来说,就必须有一条return语句。所以如果对于有返回值的Lambda表达式代码块来说如果只有一条语句那么这条语句必须是return语句,所以return关键字也可以省略
例如:
interface A
{
void info(int a);
}
先看下通过匿名内部类的形式来创建实例:
A a = new A(){
public void info(int a){
System.out.println(a);
}
}
再看通过Lambda表达式创建该实现接口的实例时:
A a = (int a)->{System.out.println(a);};

对比下,对于lambda表达式来说,省略了new A(){}段代码,只需要对抽象方法进行实现即可。且在实现方法时也不许写方法返回值类型。

然后由于形参列表允许省略形参类型所以也可以简写成:
A a = (a)->{System.out.println(a);};
由于形参列表中又只有一个参数,所以可以再次简写
A a = a->{System.out.println(a);};
由于Lambda表达式代码块中只有一条语句还可以省略方法体:
A a = a->System.out.println(a);

再看练习一个例子加深印象:
interface A
{
int info();
}

Lambda表达式进行该接口的实现类对象创建:
A a = ()->{return 1;};
由于代码块中只有一条语句,所以大括号可以省略。
A a = ()->return 1;
由于代码块中只有一个条语句,且该语句为return语句,所以关键字return可以省略。
A a = ()->1;

再看下匿名内部类和Lambda在细则上有所差别。
看案例:
定义三个函数式接口:
interface A
{
void infoA();
}

interface B
{
void infoB();
}

interface C
{
void infoC();
}

先看通过匿名内部类的形式来创建实例
}

A a = new A(){
	public void infoA()
	{
		System.out.println("匿名内部类——A");
	}
}
B b = new B(){
	public void infoB()
	{
		System.out.println("匿名内部类——B");
	}
}
C c = new C(){
	public void infoC
	{
			System.out.println("匿名内部类——C");
	}
}

}
通过观察可知,对于匿名内部类来创建的实例本身是有确切的数据类型的,只是该类在创建完该对象就消失了。

再看通过Lambda表达式的形式创建实例
A a = ()->{System.out.println(“lambda表达式A”);}
B b = ()->{System.out.println(“lambda表达式A”);}
C c = ()->{System.out.println(“lambda表达式A”);}
对于等号右边,我们可以看出它是同样的Lambda表达式写法。但是具体是什么数据类型我们并不知道。由于lambda表达式是可以正常的编译、运行的,这说明Lambda表达式实际上被当成一个任意类型(上述例子中代码一样的Lambda表达式付给的是不同类型的引用变量),Lambda表达式的具体类型要取决于运行环境的需要。
下面是重点讲解。
回顾下这段代码:
A a = ()->{System.out.println(“lambda表达式A”);}
B b = ()->{System.out.println(“lambda表达式A”);}
C c = ()->{System.out.println(“lambda表达式A”);}
可以看出Lambda表达式有一个局限性即只能实现函数式接口中的抽象方法。如果想要重写函数式接口中的默认方法或者类方法只能通过匿名内部类的形式。

由于只有为函数式接口创建实现类对象时,才可以使用Lambda表达式,所以Lambda表达式是存在某种限制条件的。
1、’Lambda表达式的目标类型必须是明确的函数式接口例如:
2、函数式接口类型 变量名 = Lambda表达式。
即Lambda表达式只能为函数式接口创建对象。

由于Lambda表达式只能为函数式接口创建对象,所以目标类型也必须是函数式接口。为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式。
1、将Lambda表达式赋值给函数式接口变量
2、将Lambda表达式作为函数式接口类型的参数传递给某个方法
3、使用函数式接口类型对Lambda表达式进行强制类型转换。这一点做一下详细的阐述:Lambda表达式的本质是创建出一个实例,那么所有的实例本质都是通过类来创建的,由于所有的类都是Object类的子类,所以理论上Lambda表达式也是可以直接赋值给Object类型的引用变量的,但是由于Lambda表达式的目标类型必须是函数式接口,所以下列语法是错误的。例如:
interface A
{
void info();
}
Object obj = ()->System.out.println(“Hello”);//由于Lambda表达式的目标类型只能是函数式接口,所以此处会报错。
解决方案为:
Object obj = (A)()->System.out.println(“Hello”);//使用函数式接口进行强制类型转换。

注意,接口是可以引用变量的。例如A a = ()->System.out.println(“Hello”);
a中存储的是一个实例对象。所以可以进行Object obj = a;
对于代码Object obj = (A)()->System.out.println(“Hello”);来说是可以看上上述二者的结合的,所以是可以这样操作的。

Lambda表达式与匿名内部类的联系和区别(引用书中原文)
Lambda表达式是匿名内部类的一种简化,因此它可以部分取代匿名内部类的作用。
Lambda表达式与匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及类成员变量(包括实例变量和类变量)。

Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。

Lambda表达式与匿名内部类主要存在如下区别
1、匿名内部类可以为任意接口创建实例。——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但Lambda表达式只能为函数式接口创建实例。
2、匿名内部类可以为抽象类甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例。
3、匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值