目录
一、lambda表达式
二、lambda表达式实现函数接口
2.1 函数式接口
2.2 lambda表达式实现无参抽象方法
2.3 lambda表达式实现有参抽象方法
2.4 lambad表达式使用代码块
三、lambda表达式调用外部变量
3.1 lambda表达式可以更改类成员变量
3.2 lambda表达式无法更改局部变量
四、异常处理
(本篇笔记整理自明日科技系列书籍)
lambda就是数学中的“λ”的读音,lambda表达式是基于λ演算而得名的,因为lambda抽象(lambda abstraction)表示一个匿名的函数,于是开发语言也将lambda表达式用来表示匿名函数,也就是没有函数名字的函数。C#、Python,甚至是C++都有lambda表达式语法。为了提高开发者的开发效率,并照顾“跨语言”开发者的开发习惯,Java语言也加入了lambda表达式。流处理是Java程序中一种重要的数据处理手段,它用少量的代码便可以执行复杂的数据过滤、映射、查找和收集等功能。
知识框架:
一、lambda表达式
1.1 lambda表达式简介
lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。
基本语法:
()->结果表达式
参数->结果表达式
(参数1,参数2,参数n)->结果表达式
也可以实现复杂方法:
()->{代码块}
参数->{代码块}
(参数1,参数2,参数n)->{代码块}
第一行实现无参方法,第二行实现只有一个参数的方法,第三行实现多参数的方法;方法体式操作符右侧代码块。
lambda表达式的语法非常抽象,并且有着非常强大的自动化功能,如自动识别泛型、自动数据类型转换等。简化理解:
() -> {代码块}
这个方法 按照 这样的代码来实现
总结:操作符左侧是方法参数,操作符右侧的是方法体。
二、lambda表达式实现函数接口
2.1 函数式接口
函数式接口指的是仅包含一个抽象方法的接口,接口中的方法非常简单地说明了接口的用途,如线程接口Runnable等。开发者可以创建自定义的函数式接口,如:
interface MyInterface{
void method();
}
如果接口中包含一个以上的抽象方法,则不符合函数式接口的规范,这样的接口不能用lambda表达式创建匿名对象。
2.2 lambda表达式实现无参抽象方法
很多函数式接口的抽象方法是无参数的,如线程接口Runnable接口只有一个run()方法,这样的无参抽象方法在lambda表达式中使用"( )"表示。
示例代码:
本实例直接在lambda表达式中创建SayHiInterface接口对象,并指定了一个字符串作为接口方法的返回值。最后在输出语句中,pi对象就是lambda表达式创建出的对象,当pi调用接口方法时就输出了lambda表达式指定的字符串。
2.3 lambda表达式实现有参抽象方法
lambda表达式中可以用“(a1,a2,a3)”的方法表示有参抽象方法,圆括号里标识符对应抽象方法的参数。如果抽象方法中只有一个参数,lambda表达式则可以省略圆括号。
运行结果如下:
相加结果:41
在这个实例中,函数式接口的抽象方法有两个参数,lambda表达式的圆括号内也写了两个参数对应的抽象方法。这里有一个点要注意,lambda表达式中的参数不需要与抽象方法的参数名称相同,但顺序必须相同。
2.4 lambad表达式使用代码块
当函数式接口的抽象方法需要实现复杂逻辑而不是返回一个简单的表达式的话,就需要在lambda表达式中使用代码块。lambda表达式会自动判断返回值类型是否符合抽象方法的定义。
三、lambda表达式调用外部变量
lambda表达式可以调用表达式以外的变量。lambda表达式无法更改局部变量的值,但是却可以更改外部类的成员变量(也可以叫做类属性)的值。
3.1 lambda表达式可以更改类成员变量
类成员变量是在lambda表达式中不是被final修饰的,所以lambda表达式可以改变其值。
创建函数式接口和测试类,在测试类中创建成员属性value和成员方法action()。在action()方法中使用lambda表达式创建接口对象,并在lambda表达式中修改value的值。运行程序,查看value值是否发生变化。
interface Vara { // 测试接口
void method(); // 测试方法
}
public class Main { // 测试类
int value = 100;
public void action() {
Vara v = () -> {
value = 1;
};
System.out.println("运行接口方法前value=" + value);
v.method(); // 运行接口方法
System.out.println("运行接口方法后value=" + value);
}
public static void main(String[] args) {
Main demo = new Main();
demo.action();
}
}
这里在学习过程中出现了错误,错误使用Vara demo = new Vara(),在代码中,Vara
是一个接口,接口是无法直接实例化的。如果想要使用接口,需要通过实现接口的类来创建实例。
结果如下:
运行接口方法前value=100
运行接口方法后value=1
从这个结果中可以看出:
①lambda表达式可以调用并修改类成员变量的值。
②lambda表达式只是描述了抽象方法是如何实现的,在抽象方法没有被调用前,lambda表达式中的代码并没有被执行,所以运行抽象方法之前类成员变量的值不会发生变化。
③只要抽象方法被调用,就会执行lambda表达式中的代码,类成员变量的值就会被修改。
3.2 lambda表达式无法更改局部变量
局部变量在lambda表达式中默认被定义为final(静态)的,也就是说,lambda表达式只能调用局部变量,却不能改变其值。
示例代码:
public class Main {
public static void main(String[] args) {
int value = 100;
Runnable runnable = () -> {
// Lambda表达式中可以读取局部变量value
System.out.println("读取局部变量value:" + value);
// 但无法修改局部变量value
// value = 200; // 编译错误
};
runnable.run();
}
}
在这个例子中,我们定义了一个局部变量value
,初始值为100。然后我们创建了一个Runnable
接口的实例,并使用Lambda表达式实现了run
方法。Lambda表达式中可以读取局部变量value
的值,但是无法修改该变量的值。如果尝试在Lambda表达式中修改value
的值,会导致编译错误。
四、异常处理
很多接口的抽象方法为了保证程序的安全性,会在定义时就抛出异常。但是lambda表达式中并没有抛出异常的语法,这是因为lambda表达式会默认抛出抽象方法原有的异常,当此方法被调用时则需要进行异常处理。
示例代码:
从这个实例中可以看出,即使lambda表达式没有定义异常,原抽象方法抛出的异常仍然是存在的,当接口对象执行此方法时会被强制要求进行异常处理。