文章目录
java8函数式编程基础 - Lambda表达式
Lambda表达式,也成为闭包(closure),是实现支持函数式编程技术的基础。
(函数的计算可以随时调用)
函数式编程 vs 面向对象编程:
函数式编程:将程序代码看作数学中的函数,函数本身作为另一个函数的参数或返回值(高阶函数)。
面向对象编程:按照真实世界客观事物的自然规律进行分析,客观世界中存在什么样的实体,构建的软件系统就存在什么样的实体。
java中方法不能单独存在,必须定义在类或接口中。
/**
* 设计一个通用方法,实现两个数值的加法和减法运算
* (考虑是一个通用方法,可以设计一个数值计算接口)
*/
public interface Calculable {
int calculateInt(int a, int b);
}
// 实现Calculable接口对象
public 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;
}
通过匿名内部类实现通用方法calculate代码很臃肿。
Lambda表达式
Lambda表达式可以替代匿名内部类。
Lambda表达式是一个匿名函数(方法)代码块,可以作为表达式、方法参数和方法返回值。
Lambda表达式所实现的匿名方法是在函数式接口中声明的。
(参数列表) -> {
// Lambda表达式体
}
Lambda表达式参数列表与接口中方法参数列表形式一样,Lambda表达式体实现接口方法。
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表达式会编译错误 The target type of this expression must be a functional interface
java8提供了 声明函数式接口注释 @FunctionalInterface
// 可计算接口
@FunctionalInterface
public interface Calculable {
// 计算两个int数值
int calculateInt(int a, int b);
}
在接口之前使用@FunctionalInterface注解修饰,当试图增加一个抽象方法时会发生编译错误。但可以添加默认方法和静态方法。
Lambda表达式简化形式
虽简化代码,但客观上使得代码可读性变差。
省略参数类型
Lambda表达式可以根据上下文环境推断出参数类型。
public static Calculable calculate(char opr) {
Calculable result;
if (opr == '+') {
// Lambda表达式实现Calculable接口
result = (a, b) -> {
return a + b;
}; // 注意分号
} else {
// Lambda表达式实现Calculable接口
result = (a, b) -> {
return a-b
}; // 注意分号
}
return result;
}
省略参数小括号
Lambda表达式中参数只有一个时
// 只举例部分
public interface Calculable {
int calculateInt(int a);
}
result = (int a) -> { // 标准形式
return a * a;
}
result = a -> { // 省略形式
return a * a;
}
省略return和大括号
如果Lambda表达式体中只有一条语句,可以省略return和大括号。
result = (int a) -> { // 标准形式
return a * a;
}
result = a -> a * a; // 省略形式
作为参数使用Lambda表达式
作为参数传递给方法。需要声明参数的类型为函数式接口类型。
public static void display(Calculate calc, int n1, int n2) {
System.out.println(calc,calculateInt(n1, n2));
}
// 调用display()
display((a, b) -> {
return a + b;
}, n1, n2);
display((a,b) -> a - b, n1, n2);
访问变量
访问成员变量
实例成员变量、静态成员变量。
此时的Lambda表达式与普通方法一样,可以读取/修改成员变量。
当访问实例成员变量或实例方法时可以使用this。
捕获局部变量
在访问外层局部变量时会发生“捕获变量”:将变量当成final的,在Lambda表达式中不能修改那些捕获的变量。
public static Calculable add() {
// 局部变量
int localValue = 20;
Calculable result = (int a, int b) -> {
// localValue++; // 编译错误
int c = a + b + localValue;
return c;
};
}
方法引用
java8后增加双冒号“::”运算符,用于“方法引用”(不是调用方法)。
虽没有直接使用Lambda表达式,但也与Lambda表达式有关,与函数式接口有关。
类型名::静态方法 // 静态方法的方法引用
实例名::实例方法 // 实例方法的方法引用
被引用方法的参数列表和返回值类型必须与函数式接口方法参数列表和方法返回值类型一致。
public class LambdaDemo {
// 静态方法,进行加法运算
// 参数列表要与函数式接口方法calculateInt(int a, int b)兼容
public static int add(int a, int b) {
return a + b;
}
}
// 下面应该放在另一个类中的...
// calc是Calculate类型,可以接受三种对象:Calculate实例对象、Lambda表达式、方法引用
public static void display(Calculate calc, int n1, int n2) {
System.out.println(calc,calculateInt(n1, n2));
}
display(LambdaDemo::add, n1, n2); // LambdaDemo::add是静态方法的方法引用
LambdaDemo d = new LambdaDemo(); // d是LambdaDemo实例
display(d::add, n1, n2); // d::add是实例方法的方法引用
// 只是将引用传递给display方法,在display方法中才真正调用方法
整合一下:
interface Calculable {
int calculateInt(int a, int b);
}
class LambdaDemo {
public static int add(int a, int b) {
return a + b;
}
}
public class Lin {
public static void main(String[] args) {
int n1 = 10;
int n2 = 5;
display(LambdaDemo::add, n1, n2);
}
public static void display(Calculable calc, int n1, int n2) {
System.out.println(calc.calculateInt(n1, n2));
}
}