Java - 带示例的 Lambda 表达式变量捕获
由 lambda 表达式的封闭范围定义的变量可在 lambda 表达式中访问。例如,lambda 表达式可以使用由其封闭类定义的实例或静态变量。 lambda 表达式还可以访问(显式和隐式),它指的是 lambda 表达式的封闭类的调用实例。因此,lambda 表达式可以获取或设置内在或静态变量的值,并调用由其封闭类定义的方法。
Java的Lambda表达式使用局部变量如上所述。
但是,当 lambda 表达式使用其封闭范围内的局部变量时,会创建一种称为变量捕获的特殊情况。在这种情况下,一个 lambda 表达式可能只使用有效最终的局部变量。一个有效的最终变量是一个在第一次赋值后其值不会改变的变量。无需将此类变量显式声明为 final,尽管这样做不会出错。
重要的是要了解封闭范围的局部变量不能被 lambda 表达式修改。这样做将取消其有效的最终状态,从而使其非法捕获。
有一些关键点需要记住,如下所示:
- 任何使用但未在 lambda 表达式中声明的局部变量、形式参数或异常参数必须声明为 final 或有效 final ,否则在尝试使用时会发生编译时错误。
- 任何在 lambda 体中使用但未声明的局部变量必须在 lambda 体之前明确赋值,否则会发生编译时错误。
- 类似的变量使用规则适用于内部类的主体。对有效最终变量的限制禁止访问动态变化的局部变量,其捕获可能会引入并发问题。相较于最终限制,它减轻了程序员的文书负担。
- 对有效最终变量的限制包括标准循环变量,但不包括增强型 for 循环变量,它们在循环的每次迭代中都被视为不同的。
以下程序说明了有效的最终变量和可变局部变量之间的区别:
示例 1
Java
// Java Program Illustrating Difference between
// Effectively final and Mutable Local Variables
// Importing reqiored classes
import java.io.*;
// An example of capturing a local variable from the
// enclosing scope
// Inrterface
interface MyFunction {
// Method inside the interface
int func(int n);
}
// Main class
class GFG {
// Main driver method
public static void main(String[] args)
{
// Custom local variable that can be captured
int number = 10;
MyFunction myLambda = (n) ->
{
// This use of number is OK It does not modify
// num
int value = number + n;
// However, the following is illegal because it
// attempts to modify the value of number
// number++;
return value;
};
// The following line would also cause an error,
// because it would remove the effectively final
// status from num. number = 9;
System.out.println("GFG!");
}
}
Java
// Java Program Illustrating Difference between
// Effectively final and Mutable Local Variables
// Importing input output classes
import java.io.*;
// Interface
interface MyInterface {
// Method inside the interface
void myFunction();
}
// Main class
class GFG {
// Custom initialization
int data = 170;
// Main driver method
public static void main(String[] args)
{
// Creating object of this class
// inside the main() method
GFG gfg = new GFG();
// Creating object of interface
// inside the main() method
MyInterface intFace = () ->
{
System.out.println("Data : " + gfg.data);
gfg.data += 500;
System.out.println("Data : " + gfg.data);
};
intFace.myFunction();
gfg.data += 200;
System.out.println("Data : " + gfg.data);
}
}
GFG!
输出说明:
正如注释所示, number 是有效的 final,因此可以在 myLambda 中使用。但是,如果要修改 number,无论是在 lambda 内部还是外部,number 都会失去其有效的最终状态。这将导致错误,并且程序将无法编译。
示例 2
Java
// Java Program Illustrating Difference between
// Effectively final and Mutable Local Variables
// Importing input output classes
import java.io.*;
// Interface
interface MyInterface {
// Method inside the interface
void myFunction();
}
// Main class
class GFG {
// Custom initialization
int data = 170;
// Main driver method
public static void main(String[] args)
{
// Creating object of this class
// inside the main() method
GFG gfg = new GFG();
// Creating object of interface
// inside the main() method
MyInterface intFace = () ->
{
System.out.println("Data : " + gfg.data);
gfg.data += 500;
System.out.println("Data : " + gfg.data);
};
intFace.myFunction();
gfg.data += 200;
System.out.println("Data : " + gfg.data);
}
}
Data : 170
Data : 670
Data : 870
Note: It is important to emphasize that a lambda expression can use and modify an instance variable from its invoking class. It just can’t use a local variable of its enclosing scope unless that variable is effectively final.