Lambda 表达示
语法规则:
(param) -> expression
或
(param) -> { //statements; }
特性:
-
可选类型声明:无需声明参数类型。 -
可选的参数圆括号:一个参数时无需定义圆括号,多个参数时则需要定义。 -
可选的大括号:方法体中只包含一条语句时无需大括号。 -
可选的返回关键字:若方法体中只有一个表达式返回值,则编译器会自动返回值,大括号需要指明表达式返回的一个值。//空参 ()->"hello java8"; ()->{return "hello java8";} //单个参数 msg->msg; (String msg)->msg; msg->{return msg;} //多个参数 (a,b)->a+b; (a,b)->{return a+b;} (int a,int b)->a+b;
注意点:
- lambda表达式主体可以有局部变量,语句。可以在主体中使用break,continue和return。也可以从主体中抛出(throw)异常。
- lambda表达式没有名称,因为它表示匿名内部类。
- lambda表达式的返回类型由编译器推断。
- lambda表达式不能使用throws子句。
- lambda表达式不能是泛型,而泛型在函数接口中定义。
- lambda表达式本身不能用作独立的表达式。
类型推断
推断lambda表达式类型的过程称为目标类型。
编译器使用以下规则来确定lambda表达式是否可分配给其目标类型:
- 它必须是一个函数接口;
- lambda表达示的参数必须与函数接口的抽象方法匹配;
- lambda表达式的返回类型与函数接口的抽象方法的返回类型兼容;
- 从lambda表达式抛出的检查异常必须与函数接口中抽象方法的已声明的throws子句兼容;
行为参数化
可以将lambda表达式作为参数传递给方法。通过其参数更改方法的行为称为行为参数化。
行为参数化模糊性:
public class Demo_02_行为参数化 {
public static void main(String[] argv) {
//注1:通过cast解决歧义
engine((IntCalculator) ((x,y)->x+y));
//注2:事先将lambda表达示分配一个函数接口解决歧义
LongCalculator lc = (x, y) -> x * y;
engine(lc);
//注3:通过指定lambda表达示参数类型解决歧义
engine((int x, int y) -> x / y);
engine((long x, long y) -> x % y);
}
private static void engine(IntCalculator ic) {
int x = 2, y = 4;
int result = ic.calc(x, y);
System.out.println(result);
}
private static void engine(LongCalculator lc) {
long x = 2, y = 4;
long result = lc.calc(x, y);
System.out.println(result);
}
@FunctionalInterface
interface IntCalculator {
int calc(int x, int y);
}
@FunctionalInterface
interface LongCalculator {
long calc(long x, long y);
}
}
上下文
lambda表达式可以只在以下四种环境中使用。
赋值上下文:显示在赋值运算符的右侧;方法调用上下文:作为方法或构造函数的参数;返回上下文:在return语句中使用lambda表达示;其目标类型在方法返回类型中声明。转换上下文:在lambda表达示前面加上强制类型转换;转换成指定的目标类型。
public class Demo_03_lambda表达示上下文 {
public static void main(String[] args) {
//1、赋值上下文
Calculator c = (a, b) -> a + b;
System.out.println(c.calc(10,20));
//2、方法调用上下文
engine((a, b) -> a * b);
//3、返回上下文
System.out.println(create().calc(100, 10));
//4、转换上下文
engine((Calculator) ((a, b) -> a % b));
}
private static Calculator create() {
return (a, b) -> a / b;
}
private static void engine(Calculator c) {
System.out.println(c.calc(10, 20));
}
@FunctionalInterface
interface Calculator {
int calc(int a, int b);
}
}
作用域
Lambda表达式不定义自己的作用域范围。若lambda中使用关键字this和super关键字在方法中,它们的行为与我们在该方法中使用它们一样。
class Scope {
public Scope() {
//int x = 0;//编译时错误,与lambda表达示变量定义冲突
Function<String, String> f = x -> {
//lambda表达示中的this代表外部类
System.out.println(this);
return x;
};
System.out.println(f.apply("hello"));
}
@Override
public String toString() {
return "Scope";
}
}
public class Demo_12_Lambda作用域 {
public static void main(String[] args) {
new Scope();
}
}
变量捕获
lambda表达示可以访问final修饰的局部变量或局部非最终初始化只有一次的变量。不能改变在lambda表达式之外定义的变量。
public class Main {
static int a = 1;
public static void main(String[] argv) {
final String b = "Hello";
//变量c只初始化一次,仍然可以在lambda表达示中使用
String c = "World";
Function<String,String> func1 = x -> {
//c = "world";编译时错误
a += 1;
return x +" "+ b + " "+ c;
};
System.out.println(func1.apply("I Love You!!!"));
}
}
主体语句
可以使用break,continue,return和throw在lambda表达式主体中的语句,但不能使用跳转语句做非局部跳转。
public class Demo_14_主体语句 {
public static void main(String[] args) {
Function<String, String> f = x -> {
for (int i = 0; i < 10; i++) {
System.out.print(i);
if (i == 2) {
System.out.println();
break;//只跳出当前循环,并未跳出当前lambda表达示范围
}
}
return x;
};
System.out.println(f.apply("Akor"));
for (int i = 0; i < 10; i++) {
System.out.print(i);
if (i == 4) {
Function<String, String> f2 = y -> {
//break; 编译时报错,这条语句试图跳出当前lambda表达示语句
return y + " World!!";
};
System.out.println(f2.apply("Hello"));
}
}
}
}
递归Lambda
使用递归函数作为方法引用来创建lambda表达式。最后的lambda表达式成为递归。
public class Main {
public static void main(String[] args) {
// 方法引用
IntFunction<Long> factorialCalc = Main::factorial;
System.out.println(factorialCalc.apply(10));
}
public static long factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
}
本文深入解析了Lambda表达示的语法规则,包括可选类型声明、参数圆括号和大括号的使用,以及返回关键字的省略。还介绍了类型推断、行为参数化和lambda表达式在不同上下文的应用。重点讲解了作用域、变量捕获、主体语句和递归Lambda的使用。
1165

被折叠的 条评论
为什么被折叠?



