在java中,我们实现接口的时候,可以通过实现匿名内部类,但是匿名内部类的写法是有点繁琐的,所以java8中新推出了一个简单化的表达lambda表达式。
lambda表达式就是允许使用更简洁的代码来创建只有一个抽象方法的接口的实例。
而只有一个抽象方法的接口又被称为函数式接口。
先展示一个最简单的:
public class shape {
public static void main(String[] args) {
a a = new a() {
public void b() {
}
};
a b = () -> {
};
}
}
interface a {
void b();
}
我们可以发现,相较于传统的new的方法,使用lambda表达式明显简便很多。
其中()小括号,表示形参列表,{}大括号表示方法体,->只是连接两者。
那么lambda表达式具体用法是什么呢?
- 如果没有形参列表,方法体一句没有,需要小括号和大括号
- 如果方法体只有一句,可以省略大括号,且写在同一行。
- 如果仅有返回值这一句,可以省略大括号和return
- 如果你想写上return,那你就需要用大括号包起来。
- 如果方法体有多句,不可以省略大括号,不能写在同一行。
- 如果有一个形参列表,可以省略小括号,可以省略数据类型
- 如果有多个形参列表,可以省略数据类型,不能省略小括号
- 注意,对于省略数据类型,如果数据类型有注解修饰,不能省略
- lambda表达式实际上为任意类型对象,根据运行的需要来调整对象,
- 通过var变量必须指定类型
- 注意,如果是方法的重载,那么不能用lambda表达式,他有两个抽象方法,在实现的时候也需要全部重写。
这么看是不是有一点抽象,建议大家看下面的代码和注释:
public class shape {
public static void main(String[] args) {
//普通的匿名内部类重写方式
a a = new a() {
public void b() {
}
};
//如果没有形参列表,方法体一句没有,需要小括号和大括号
a b = () -> {
};
//如果方法体只有一句,可以省略大括号,且写在同一行。
a c = () -> System.out.println("a");
//如果仅有返回值这一句,可以省略大括号和return
c e = () -> 1;
//如果你想写上return,那你就需要用大括号包起来。
// c f = () -> {return 1};这样写是不对的
c f = () -> {
return 1;
};
c g = () -> {
System.out.println("1");
return 1;
};
//如果方法体有多句,不可以省略大括号,不能写在同一行。
a d = () -> {
System.out.println("a");
System.out.println("b");
};
//如果有一个形参列表,可以省略小括号,可以省略数据类型
b h = qq -> System.out.println("a");
//如果有多个形参列表,可以省略数据类型,不能省略小括号
d i = (haha,gg) ->System.out.println("a");
//注意,这里的qq,haha,gg都是变量,传给形参,不能直接用数值
//注意,对于省略数据类型,如果数据类型有注解修饰,不能省略
//lambda表达式实际上为任意类型对象,根据运行的需要来调整对象,
//这里也必须指明,因为他会根据前面的Object来判断,而Object不是一个接口
Object obj = (a) () -> System.out.println("1");
// 通过var变量必须指定类型
var gg = (a) () -> System.out.println("1");
//注意,如果是方法的重载,那么不能用lambda表达式,他有两个抽象方法,在实现的时候也需要全部重写。
e j = new e() {
public void f() {
}
public void f(int i) {
}
};
}
}
interface a {
void b();
}
interface b {
void c(int a);
}
interface c{
int d();
}
interface d{
void e(int a,float b);
}
interface e{
void f();
void f(int i);
}
对于函数式接口,java中为了方便见名知意,定义了一部分命名规范。
- xxxFunction:通常包含accept抽象方法,对参数进行处理,然后返回一个新的值。
- xxxConsumer:也是包含accept抽象方法,对参数处理,但是不返回返回值。
- xxxPredicate:包含一个test抽象放啊,对参数进行判断然后返回一个boolean值。
- xxxSupplier:包含一个getAsXXX抽象方法,不用输入参数,按照某种逻辑返回一个数据。
- 首先,最明显的一点是,lambda表达式只能实现函数式接口,而匿名内部类能实现任意的类或者接口。
- 匿名内部类重写的接口方法的方法体中不能调用接口中的default方法!!!!,注意只是方法体。
这个点咋一听似乎就是这么回事,但是经过我的代码测试,我发现了一点问题:
public class shape{
public static void main(String[] args) {
a c = new a(){
@Override
public void a(){
this.hashCode();
b();
}
};
a d = () -> {
// this.hashCode();//编译错误
// b();//编译错误
};
}
}
@FunctionalInterface
interface a{
void a();
default void b(){
System.out.println(this.hashCode());
}
}
为什么lambda表达式实现的抽象方法的方法体中不能调用默认方法呢?
通过测试,我发现他不仅不可以调用默认方法,甚至不能调用当前对象的方法,比如说hashcode什么的。
为什么呢?
因为这是根据lambda表达式的词作用域有关的,lambda表达式虽然也会构建一个类,但是内部却没有自己的this指针,当在内部使用this指针时,代指的是表达式外部对应的对象。
this.hashcode()报错是因为当前main是static方法,所以无法调用当前类的this指针
b() 报错也是因为shape类中没有b()方法,因为lambda的词作用域是当前类
在Java官方文档里有这样一句话:
Lambda expressions are lexically scoped. This means that they do not inherit any names from a supertype or introduce a new level of scoping. Declarations in a lambda expression are interpreted just as they are in the enclosing environment.
就是说lambda表达式为词法作用域,即它不会从父类继承任何名字,也不会引入新的作用域。
当然要是理解不了,记住就可以了。



