目录
最常见的:作为方法的参数,此时这个方法的参数需要声明为函数式接口
1. lambda表达式与匿名内部类
匿名内部类
可以说,lambda表达式就是一种特殊的匿名内部类。(类似于接口和抽象类的关系)换句话说,我们学习匿名内部类的主要作用就是为了引出lambda表达式,因为它更常用。。
回顾一下匿名内部类:
class FatherClass { // 匿名内部类必须要继承自一个父类,所以必须首先建立一个父类(因为它是匿名的,所以只能靠父类变量指向它)
int method1(...) {...};
int method2(...) {...};
}
class OuterClass {
// 定义一个匿名类
FatherClass father = new FatherClass() { // 1. 如果后面带的不是{...},而是直接以;作为结束,那么就是创建了一个FatherClass的实例。 2. 但这里后面跟的是{...},所以它是一个继承自FatherClass的匿名类(没有具体的名字),并且用父类的变量指向它
@OverRide
int method1(...) {...}; // 匿名类代码,重写父类中的method1和method2方法
@OverRide
int method2(...) {...}
int method3(...) {...} // 还可以新定义方法
};
father.method1(...); // 在外部类中可以调用方法,根据多态性质可以知道,method1和method2都是子类中的实现决定的
father.method2(...);
father.method3(...);
}
下面可以看一个匿名内部类的典型使用方法:
// 可计算接口
interface Calculable {
// 计算两个int数值
int calculateInt(int a, int b);
}
public class Main {
/**
* 通过操作符,进行计算
* @param opr 操作符
* @return 实现Calculable接口的对象
*/
static Calculable calculate(char opr) {
Calculable result;
if (opr == '+') {
// 匿名内部类实现Calculable接口
result = new Calculable() {
// 实现加法运算
@Override
public int calculateInt(int a, int b) {
return a + b;
}
};
} else {
// 匿名内部类实现Calculable接口
result = new Calculable() {
// 实现减法运算
@Override
public int calculateInt(int a, int b) {
return a - b;
}
};
}
return result;
}
public static void main(String[] args) {
int n1 = 10;
int n2 = 5;
// 实现加法计算Calculable对象
Calculable f1 = calculate('+');
// 实现减法计算Calculable对象
Calculable f2 = calculate('-');
// 调用calculateInt方法进行加法计算
System.out.println(n1 + "+" + n2 + "=" + f1.calculateInt(n1, n2));
// System.out.printf("%d + %d = %d \n", n1, n2, f1.calculateInt(n1, n2));
// 调用calculateInt方法进行减法计算
System.out.println(n1 + "-" + n2 + "=" + f2.calculateInt(n1, n2));
// System.out.printf("%d - %d = %d \n", n1, n2, f2.calculateInt(n1, n2))
}
}
输出结果为:
10+5=15
10-5=5
lambda表达式
从上面可以看到,使用匿名类代码很臃肿,因此可以使用lambda代替匿名内部类(反正都没名字嘛!),lambda表达式的典型语法如下:
(参数列表) -> {
// Lambda表达式体
};
其余代码不变,只要将calculate方法中的匿名内部类使用lambda表达式替代成如下即可:
/**
* 通过操作符,进行计算
* @param opr 操作符
* @return 实现Calculable接口对象
*/
public static Calculable calculate(char opr) {
Calculable result;
if (opr == '+') {
// Lambda表达式实现Calculable接口
result = (int a, int b) -> {
return a + b;
};
} else {
// Lambda表达式实现Calculable接口
result = (int a, int b) -> {
return a - b;
};
}
return result;
}
可以发现简洁了不少。
但是使用lambda表达式还是有一定限制条件的,具体注意点如下:
- 与匿名内部类不同,lambda表达式只能实现接口,而不能继承类,并且它只能实现函数式接口。什么是函数式接口?所谓函数式接口,就是指在这个接口中有且只有一个抽象方法,即只能有一个抽象方法!!!如果声明了多个抽象方法,那么在实现lambda表达式时会编译出错。
- 为了避免在接口中定义多个抽象方法,可以在定义接口时在头上添加@FunctionalInterface注解,如:
// 可计算接口 @FunctionalInterface public interface Calculable { // 计算两个int数值 int calculateInt(int a, int b); }
在接口之前使用 @FunctionalInterface 注解修饰,那么试图增加一个抽象方法时会发生编译错误。
由此可以总结,要想定义一个lambda表达式应该进行如下几个步骤:
- 首先,定义一个函数式接口
// 定义一个函数式接口 @FunctionalInterface public interface Calculable { // 计算两个int数值 int calculateInt(int a, int b); }
- 定义一个外部类,并在外部类中定义一个函数式接口的变量
- 让这个变量指向lambda表达式,始终要记住:lambda表达式返回的是一个实现了这个接口的子类对象实例!!!并且在lambda表达式内部是实现了这个接口中定义的方法!!
- 最后,我们再调用这个lambda表达式:
输出:10+5=15
2. lambda表达式的3种简写方式
省略参数类型
Lambda 表达式可以根据上下文环境推断出参数类型。calculate 方法中 Lambda 表达式能推断出参数 a 和 b 是 int 类型,简化形式如下:
省略参数列表的小括号
如果 Lambda 表达式中的参数只有一个,可以省略参数小括号。修改 Calculable 接口中的 calculateInt 方法,代码如下:
// 可计算接口
@FunctionalInterface
public interface Calculable {
// 计算一个int数值
int calculateInt(int a);
}
省略return和大括号
如果 Lambda 表达式体中只有一条语句,那么可以省略 return 和大括号,代码如下:
3. 什么时候需要使用lambda表达式?
最常见的:作为方法的参数,此时这个方法的参数需要声明为函数式接口
public static void main(String[] args) {
int n1 = 10;
int n2 = 5;
// 打印加法计算结果
display((a, b) -> {
return a + b;
}, n1, n2);
// 打印减法计算结果
display((a, b) -> a - b, n1, n2);
}
/**
* 打印计算结果
*
* @param calc Lambda表达式
* @param n1 操作数1
* @param n2 操作数2
*/
public static void display(Calculable calc, int n1, int n2) {
System.out.println(calc.calculateInt(n1, n2));
}
访问变量
因为lambda表达式也是一种特殊的内部匿名类,所以具有与匿名内部类一样的访问方法。即,它可以访问外部类的所有成员。但如果位于一个方法中,则其只能访问方法中 final 类型的局部变量和参数。
例1:访问成员变量
public class LambdaDemo {
// 实例成员变量
private int value = 10;
// 静态成员变量
private static int staticValue = 5;
// 静态方法,进行加法运算
public static Calculable add() {
Calculable result = (int a, int b) -> {
// 访问静态成员变量,不能访问实例成员变量
staticValue++;
int c = a + b + staticValue;
// this.value;
return c;
};
return result;
}
// 实例方法,进行减法运算
public Calculable sub() {
Calculable result = (int a, int b) -> {
// 访问静态成员变量和实例成员变量
staticValue++;
this.value++;
int c = a - b - staticValue - this.value;
return c;
};
return result;
}
}
例2:访问局部变量
public class LambdaDemo {
// 实例成员变量
private int value = 10;
// 静态成员变量
private static int staticValue = 5;
// 静态方法,进行加法运算
public static Calculable add() {
// 局部变量
int localValue = 20;
Calculable result = (int a, int b) -> {
// localValue++;
// 编译错误
int c = a + b + localValue;
return c;
};
return result;
}
// 实例方法,进行减法运算
public Calculable sub() {
// final局部变量
final int localValue = 20;
Calculable result = (int a, int b) -> {
int c = a - b - staticValue - this.value;
// localValue = c;
// 编译错误
return c;
};
return result;
}
}