javac数据流分析的诡异误报
Posted
技术标签:
【中文标题】javac数据流分析的诡异误报【英文标题】:Weird false-positive of javac data flow analysis 【发布时间】:2012-02-25 03:25:36 【问题描述】:我有以下形式的代码:
class Test
private final A t;
public Test()
for ( ... : ... )
final A u = null;
t = new A();
private class A
编译器说:
variable t might already have been assigned
有趣的是,如果我对循环执行以下任何更改,它就会成功!
将循环内容更改为A u = null
删除循环(但保留final A u = null;
)
将 foreach 样式的循环替换为经典的计数循环
这是怎么回事?
注意:我无法获得导致错误的最小示例,因此“环境”(大约 1400 位置)可能有问题。不过,我看不出有什么会干扰t
的初始化,因为t
没有写入其他任何地方。
有趣的事实:如果我删除它,IntelliJ IDEA 会说“变量 'u' 可以有 'final' 修饰符...”。
我使用 javac 1.6.0_26。
更新:你去吧,这个例子是如此如此最小:
import java.util.List;
class A
private final boolean a;
public A()
for ( final Object o : new Object[] )
final Object sh = null;
a = true;
class B
private final Object b1;
private final Object b2;
B()
b1 = null;
b2 = null;
在javac 1.6.0_26
上编译失败,但在javac 1.7.0_02
上编译。所以我想我遇到了一些邪恶的角落案例......什么?
请注意,您可以执行任何操作
删除任何一个成员 在A()
的循环中删除final
用普通的for
循环替换循环,例如for ( int i=0; i<100; i++ ) ...
它会编译。
【问题讨论】:
您可能有自定义编译器设置,您使用的是哪个 IDE? 我跑mvn install
;我们的项目没有定义编译器参数(afaik)。
对我来说似乎是一个 IntelliJ 问题。 Eclipse 对我来说没有出现这个问题。当然,如果循环没有引用t
,那么它不应该在以后影响它。
CLI maven 显示错误。我同意它不应该,但确实如此。
【参考方案1】:
如果你有很多代码,我会试试这个。
private final A t;
public Test()
final int t = 1;
for ( ... )
final A u = null;
this.t = new A();
这将导致任何“可能”初始化 t
的代码失败(并显示在编译器中。
【讨论】:
正如我在问题中所说,Test#t
没有其他写信。我通过用法搜索、字符串搜索和 - 只是为了逗你开心 - 按照你的建议通过引发编译器错误来确认这一点。
谢谢,您的回复很有趣。 ;)【参考方案2】:
如果你的构造函数碰巧调用了另一个本身没有设置t
的构造函数,编译器将无法理解。
见here。
【讨论】:
感谢您的回答。我其实也想过这个,但是不,没有这样的电话。其实我只定义了这一个构造函数。我真正的课程是***课程,不扩展任何其他课程(但Object
),因此在这方面不应该有任何干扰。它实现了一个接口,但这不应该受到伤害。
@Raphael 我猜你最好提供一个SSCCE,但它当然不应该编译......它在 IntelliJ 中编译得很好。
如果我知道基本部分,我就会知道问题所在,你不觉得吗? ;) 我不知道外部的东西会如何影响这一点,所以我不知道从哪里开始添加/删除东西。不过,我正在尝试。
我花了一段时间,但我现在有一个小例子。见上文。【参考方案3】:
由于该问题在 Java 7 中已修复,因此可能是 Java 6 编译器中的错误。
【讨论】:
【参考方案4】:据我了解,将对象存储在 final var 中并不会使您的对象不可变,而是使其引用。这解释了为什么当您删除 final 关键字时它会起作用,并且根据删除 for 循环,我认为您正在访问对象引用而不是实例。
【讨论】:
u
在其初始化后永远不会被访问或写入(以仍然无法编译的真实代码的简化形式)。以上是关于javac数据流分析的诡异误报的主要内容,如果未能解决你的问题,请参考以下文章