public class ArraysDemo {
public static void main(String[] args) {
int[] a = {0, 2, 4, 6, 8};
int[] b = {10, 12, 14, 16, 18};
Arrays.setAll(a, i -> b[i]+1);
System.out.println(Arrays.toString(a));
}
}
输出:[11, 13, 15, 17, 19]
使用的setAll()
函数的来源如下:
public static void setAll(int[] array, IntUnaryOperator generator) {
Objects.requireNonNull(generator);
for (int i = 0; i < array.length; i++)
array[i] = generator.applyAsInt(i);
}
IntUnaryOperator
是一个功能界面,这是其来源的一部分:
public interface IntUnaryOperator {
int applyAsInt(int operand);
// rest methods are omitted
}
如果我错了,请纠正我,但我对Java中lambda表达式的理解是当我将lambda表达式作为参数传递给setAll()
方法时,实现{{1的匿名类的对象创建接口并将其称为IntUnaryOperator
。 lambda表达式本质上是generator
方法的一个实现,所以我相信它会转化为:
applyAsInt()
我觉得它可以访问int applyAsInt(int operand){
return b[operand]+1;
}
,因为它在operand
中作为参数传递但是,我不知道它如何操纵array[i] = generator.applyAsInt(i);
- 它不是作为参数传递,所以如何引用它?我错过了什么?
答案 0 :(得分:5)
原因是b
有效最终。
可以使用和更改实例和静态变量 限制在一个lambda的身体。使用局部变量, 但是,更受限制:不允许捕获局部变量 除非它们是有效的最终版本,否则Java 8中引入了一个概念。 非正式地,局部变量如果是初始值则实际上是最终的 永远不会改变(包括在lambda表达式的主体内)-in 换句话说,声明最终不会导致编译失败。 有效终结的概念不会引入任何新的语义 到Java;它只是一种稍微冗长的定义最终版本的方式 变量
来自http://www.lambdafaq.org/can-lambda-expressions-use-variables-from-their-environment/
答案 1 :(得分:2)
Arrays.setAll(a, i -> b[i]+1);
相当于:
的效果 Arrays.setAll(a, new IntUnaryOperator() {
private int[] b$ = b;
@Override
public int applyAsInt(int i);
return b$[i]+1;
}
});
这是一个变量b
,其名称和价值与原始b
相同。需要此副本,因为原始变量(或新变量)的寿命可能比另一个变量更有限。例如,是一个局部变量。
要隐藏现在有两个变量,请求不再将它们分配(“有效最终”),这会导致“相同”变量的值不同。
答案 2 :(得分:1)
即使没有lambda表达式,例如使用匿名内部类,您可以捕获周围环境的值,即
int[] a = {0, 2, 4, 6, 8};
int[] b = {10, 12, 14, 16, 18};
Arrays.setAll(a, new IntUnaryOperator() {
public int applyAsInt(int i) {
return b[i]+1;
}
});
但有一些差异,但与您的问题无关。当使用匿名内部类时,关键字this
和super
引用内部类的实例,而在lambda表达式中,它们具有与周围上下文相同的含义。此外,当内部类访问周围类的private
成员时,编译器将插入辅助方法来执行访问,而lambda表达式可以自然地访问包含类的theur成员。
为了实现这一点,lambda表达式i -> b[i]+1
的代码将被编译为具有以下形式的你的类的合成方法:
private static int lambda$main$0(int[] b, int i) {
return b[i]+1;
}
由于它是您班级中的一种方法,因此可以访问所有成员。
因此在运行时生成的类等同于
final class ArraysDemo$Lambda$1 implements IntUnaryOperator {
final int[] b;
ArraysDemo$Lambda$1(int[] b) {
this.b = b;
}
public int applyAsInt(int i) {
return ArraysDemo.lambda$main$0(b, i);
}
}
关注等价这个词,并不是说它必须完全像这样(更不用说普通Java类无法访问private
方法的事实了ArraysDemo.lambda$main$0
)。
但这些只是技术细节,最重要的关键点是lambda表达式可以有效地访问最终的局部变量及其包含类的成员,并且编译器和运行时环境将确保它作品。您可以简单地将它们视为在定义它们的上下文中进行评估的函数。