概述
什么是lambda表达式
lambda表达式也被称为闭包、箭头函数、匿名函数,是JDK8引入的重要特性之一
lambda表达式体现的是轻量级的函数式编程思想
lambda基础知识
函数式接口
既然是讲lambda的,为什么这里要提到函数式接口的内容呢?
这是因为,Java中的lambda表达式,其实就是针对函数式接口的一个封装实现。
简单来说,函数式接口就是只有一个抽象方法的接口。
上例中的Runnable就是一个函数式接口。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@FunctionalInterface
是语义化验证注解,如果该接口不是函数式接口,则编译器会提示错误。
上文说到,函数式接口是只有一个抽象方法的接口,这么说还不够完整。
函数式接口还可以包含默认方法和静态方法和来自object继承的方法。
默认方法:
@FunctionalInterface
interface IUserVerify{
void userVerify(String name,int age);
default String getUserRole(String name){
if ("admin".equals(name)){
return "系统管理员";
} else{
return "普通用户";
}
}
}
再加上静态方法
@FunctionalInterface
interface IUserVerify{
void userVerify(String name,int age);
default String getUserRole(String name){
if ("admin".equals(name)){
return "系统管理员";
} else{
return "普通用户";
}
}
static boolean isValid(String name){
return !TextUtils.isEmpty(name);
}
}
至于第三种来自object继承的方法,这里就不举例子了,大家应该尽量避免,以免造成歧义。
简单用法如下:
public class LambdaDemo implements IUserVerify{
public static void main(String[] args) {
IUserVerify verify = new LambdaDemo();
String name = "admin";
if (IUserVerify.isValid(name)){
System.out.println(verify.getUserRole(name));
}
}
@Override
public void userVerify(String name, int age) {
}
}
语法格式
(parameters) -> expression
或
(parameters) ->{ statements; }
特性
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。个数及顺序和绑定的接口中的抽象方法一致。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
常见用法如下:
public class LambdaDemo {
public static void main(String[] args) {
//匿名内部类形式 有冗余代码
ILambda lambda0 = new ILambda() {
@Override
public void test() {
System.out.println("匿名内部类形式");
}
};
lambda0.test();
//不带参数的lambda形式,如果方法体中只有一行,可省略大括号
ILambda lambda = () -> System.out.println("lambda形式");
lambda.test();
//带参数lambda形式
ILambda1 lambda1 = (name, age) -> {
System.out.println(name + "的年龄是" + age);
};
lambda1.test("laowang",18);
//带参数带返回值的lambda形式
ILambda2 lambda2 = (name, age) -> {
return name + "的年龄是" + age;
};
System.out.println(lambda2.test("tom",14));
}
}
/**
* 不带参数不带返回值的函数式接口
*/
interface ILambda{
void test();
}
/**
* 带参数不带返回值的函数式接口
*/
interface ILambda1{
void test(String name,int age);
}
/**
* 带参数带返回值的函数式接口
*/
interface ILambda2{
String test(String name,int age);
}
变量捕获
匿名内部类的变量捕获
- 匿名内部类中可以直接访问全局变量、局部变量和内部变量
- 局部变量不可修改,默认推导变量的修饰符为
final
- this指代的作用域为当前的内部类
public class LambdaDemo {
String s1 = "全局变量";
public void testInner(){
String s2 = "局部变量";
new Thread(new Runnable() {
String s3 = "内部变量";
@Override
public void run() {
s1 = s1 + "a";
//全局变量可以直接访问,不能用this,因为在匿名内部类中,this指的是当前内部类型的对象
System.out.println(s1);
//局部变量也可以直接访问,但是不能对局部变量进行修改
System.out.println(s2);
System.out.println(s3);
System.out.println(this.s3);
}
}).start();
}
public static void main(String[] args) {
LambdaDemo demo = new LambdaDemo();
demo.testInner();
}
}
输出结果如下:
全局变量a
局部变量
内部变量
内部变量
lambda表达式变量捕获
- 全局变量可以通过this访问
- 局部变量可以直接访问,但不可修改,默认推导变量的修饰符为
final
。 - 内部变量可以直接访问,但不可通过this访问。
我们知道 this
表示的是所属方法所在类型的对象,由此可知,lambda并没有创建一个对象级的作用域。
public class LambdaDemo {
String s1 = "全局变量";
public void testLambda(){
String s2 = "局部变量";
new Thread(() -> {
String s3 = "内部变量";
//访问全局变量,可以修改,this 表示的是所属方法所在的类型的对象,由此可知,lambda并没有创建一个对象类型的作用域。
System.out.println(this.s1);
//直接访问局部变量,不可修改
System.out.println(s2);
//直接访问内部变量,可以修改
System.out.println(s3);
}).start();
}
public static void main(String[] args) {
LambdaDemo demo = new LambdaDemo();
demo.testLambda();
}
}
输出结果如下:
全局变量
局部变量
内部变量
lambda 类型检查
重载方法不能使用lambda表达式,因为lambda表达式存在类型检查,会自动推导lambda表达式的目标类型。而重载方法会让JVM无法推导出正确的目标类型。