废话不多说,我来写代码:
public class A {
static Object f() {
String str = "hello";
str = "world"; // (1) ошибка компиляции!
class X extends Object {
public String toString() {
return str;
}
}
return new X();
}
static Object g() {
String[] s = { "hello", "silence" };
s[0] = "World"; // (2) НЕТ ошибки компиляции.
class X extends Object{
public String toString() {
return s[0];
}
}
return new X();
}
public static void main(String[] args) {
}
}
注释掉该行(1)会让编译器知道实际上str有f()final,代码将编译。您无需
注释掉该行。(2)毕竟s,对一个不会改变的数组的引用。采取s[0]然后是合法的。
问题:为什么我们要求在本地类中只使用最终或有效的最终局部变量?我可以使用数组的第一个单元格,s一切都会好起来的。
我的想法和误解:类对象的代码一般会是什么样子f().X?它将是一个带有代码的对象Object,但它的方法toString()将是这样的(伪代码):
public String toString() {
return 0x47e9c2dd;
}
据我了解,0x47e9c2dd那条线的地址在str哪里?但是如果它是这样工作的,那么当我们创建类的实例时,我们将在这个方法中放入什么地址有什么区别f().X......
toString()我也不明白:我们通过引用局部变量str定义了一个类方法f().X,它(作为引用)会在工作完成后消失f(),但同时,从返回的对象f()包含在它的toString()引用中str(作为一个东西)。所以我们花费 RAM 来保持方法的f().X.toString()定义?....
总的来说,我很困惑,我绝对不明白这些本地类发生了什么,因为这个对 final 或有效 final 的奇怪要求被分配给了我们。请解释一下这是怎么回事...
在 Java 中,闭包捕获值,而不是变量。编译内部类时,Java 将在其中创建一个字段来存储从外部范围“捕获”的变量的值:
要求两个变量都是不可变的有几个原因。
我确信最重要的是 Java 被定位为一种语言,它从根本上抑制了犯很多错误并迫使程序员编写正确代码的可能性。而经典的闭包并不是最简单、最直观的话题。因此,在 Java 中,闭包被故意限制并变得更简单。
此外,如果您实现经典闭包,则有必要以某种方式保存捕获的变量。例如,在堆上分配空间。首先,这将需要完成虚拟机,并且可能需要更改字节码。其次,这是内存泄漏的一个原因。
可以使该字段
val$str可变,但这会使匿名类和 lambda 表达式在多线程环境中的使用变得复杂并减慢速度。可以让程序员有机会选择这个字段何时是最终的,何时不是,但它不再是隐含的,我们再次需要改进虚拟机,使语言复杂化并导致错误。可以不从外部范围限制变量的可变性,但是在语言层面
str只有一个变量,它在不同范围内可以有不同值的可能性是错误的更大原因比经典的闭包。总之,不变性约束使语言更好,闭包的技术实现更容易。
您可以从 Brian Goetz 在“ State of the Lambda: Variable capture ”中读到一些关于此的内容。