我最近遇到了错误消息“空白的最终字段obj可能尚未初始化”。
如果您尝试引用可能尚未分配值的字段,通常就是这种情况。示例类:
public class Foo {
private final Object obj;
public Foo() {
obj.toString(); // error (1)
obj = new Object();
obj.toString(); // just fine (2)
}
}
我使用Eclipse。在该行中,(1)
我得到了错误,在该行中,(2)
一切正常。 到目前为止,这是有道理的。
接下来,我尝试obj
在构造函数内部创建的匿名接口中访问。
public class Foo {
private Object obj;
public Foo() {
Runnable run = new Runnable() {
public void run() {
obj.toString(); // works fine
}
};
obj = new Object();
obj.toString(); // works too
}
}
这也行得通,因为obj
在创建界面的那一刻我无法访问。我也可以将实例传递到其他地方,然后初始化对象obj
,然后运行接口。(但是,null
在使用之前进行检查是适当的)。
还是有道理的。
但是现在,我Runnable
使用 lambda表达式将 实例 的创建缩短为 burger-arrow版本:
public class Foo {
private final Object obj;
public Foo() {
Runnable run = () -> {
obj.toString(); // error
};
obj = new Object();
obj.toString(); // works again
}
}
这是我无法再关注的地方。在这里,我再次得到警告。我知道,编译器不会像通常的初始化那样处理lambda表达式,它不会“将其替换为长版本”。但是,为什么这会影响以下事实:run()
在Runnable
对象创建时我没有在方法中运行代码部分?
在 调用 之前,
我仍然可以进行初始化run()
。因此从技术上讲,这里可能不会遇到NullPointerException
。(尽管也最好在null
这里进行检查。但这是另一个约定。)
我犯了什么错误?lambda的处理方式有何不同,以至于它影响我的对象使用方式?
感谢您的进一步解释。
我无法使用Eclipse的编译器重现您最后一个案例的错误。
但是,我可以想象的Oracle编译器的理由如下:在lambda中,obj
必须在声明时捕获的值。也就是说,在lambda主体中声明它时必须对其进行初始化。
但是,在这种情况下,Java应该捕获Foo
实例的值而不是obj
。然后,它可以obj
通过(初始化的)Foo
对象引用进行访问并调用其方法。Eclipse编译器就是这样编译您的代码。
这在规范中有所暗示,在这里:
方法参考表达式评估的时间比lambda表达式(第15.27.4节)要复杂。当方法引用表达式的::分隔符之前具有表达式(而不是类型)时,将立即对该子表达式求值。
存储评估结果,直到调用相应功能接口类型的方法为止
;此时,结果将用作调用的目标参考。这意味着::分隔符之前的表达式仅在程序遇到方法引用表达式时才被评估,并且不会在后续对功能接口类型的调用时被重新评估。
类似的事情发生了
Object obj = new Object(); // imagine some local variable
Runnable run = () -> {
obj.toString();
};
想象一下obj
,当执行lambda表达式代码时,它是一个局部变量,将obj
被求值并生成一个引用。此引用存储在Runnable
创建的实例的字段中。当run.run()
被调用时,例如使用存储的参考值。
如果obj
未初始化,则不会发生这种情况。例如
Object obj; // imagine some local variable
Runnable run = () -> {
obj.toString(); // error
};
Lambda无法捕获的值obj
,因为它尚无值。它实际上等效于
final Object anonymous = obj; // won't work if obj isn't initialized
Runnable run = new AnonymousRunnable(anonymous);
...
class AnonymousRunnable implements Runnable {
public AnonymousRunnable(Object val) {
this.someHiddenRef = val;
}
private final Object someHiddenRef;
public void run() {
someHiddenRef.toString();
}
}
这就是Oracle编译器当前如何运行您的代码片段。
但是,Eclipse编译器不是捕获的值obj
,而是捕获this
(Foo
实例的)值。它实际上等效于
final Foo anonymous = Foo.this; // you're in the Foo constructor so this is valid reference to a Foo instance
Runnable run = new AnonymousRunnable(anonymous);
...
class AnonymousRunnable implements Runnable {
public AnonymousRunnable(Foo foo) {
this.someHiddenRef = foo;
}
private final Foo someHiddenFoo;
public void run() {
someHiddenFoo.obj.toString();
}
}
这很好,因为您假设Foo
实例在run
调用时已完全初始化。
问题内容: 我正在用Java编程。我已经在每种方法中添加了注释,以解释它们应该做什么(根据分配)。我将我所知道的添加到了存根(这是我在研究学校提供的javadoc之后创建的)。我的问题不是几个函数,我知道testWord和setWord中有错误,但是我自己解决。我的问题是关于这条线的: 这行是由学校提供的,因此我必须假设它是正确的,我在任何地方都找不到关于常量字段值INITIAL的任何文档,因此,
问题内容: 这个 自我回答的问题是由于变量“snackbar”可能尚未初始化而引起的。我认为还有更多细节,最好与该特定问题分开添加。 为什么以下代码无法编译? 编译错误: 问题答案: 发生这种情况的原因是实现匿名类的方式。如果对代码稍作更改然后反编译,则可以看到以下内容: 即,使匿名类引用不同的局部变量。现在可以编译了;我们可以使用进行反编译并查看匿名类的接口: (是Java内部引用匿名类的名称)
我正在研究Euler Problem 9项目,其中说明: 毕达哥拉斯三元组是由三个自然数组成的集合 例如,3^2 4^2=9 16=25=52。 确实存在一个毕达哥拉斯三重态,其bc=1000。查找产品abc。 以下是我到目前为止所做的: 当我运行代码时,会出现以下错误: 注意:我的每个变量(a、b和c)都有不同的行号。 我想当我声明a、b和c为整数时,如果不赋值,默认值是0。 即使不是这样,在我
问题内容: 我有一个方法创建一个,另一个方法更改字符串 我的编译器说它“可能尚未初始化”。 有人可以解释吗? 问题答案: 变量可能尚未初始化 在内部定义方法时,必须在其中初始化程序的每个变量中必须先使用一个值的地方。 同样重要的是,您的代码将永远无法正常运行,因为Java中的字符串是不可变的,因此您无法编辑字符串,因此应更改方法。 我将您的代码更改为类似的内容,但是我认为您的编辑方法应该做另一件事
问题内容: 当我尝试编译时: 我得到这些错误: 在我看来,我在方法的顶部初始化了它们。怎么了 问题答案: 你声明了它们,但没有初始化它们。初始化它们是将它们设置为等于一个值: 因为未初始化变量,但在循环中增加了变量(例如),因此会收到错误消息。 Java原语具有默认值,但如下一位用户所述 当声明为类成员时,它们的默认值为零。局部变量没有默认值
所以我已经在结果中声明了相关字符串的值,但是它仍然说我的变量“result”可能还没有初始化。 我正在尝试实现与此类似的输出。任何人都可以帮我吗?谢谢!