eclipse编译器拒绝编译以下代码,声明字段s不可见。 (IBM的Aspect J编译器也拒绝,说“无法解决”)为什么会这样?
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
Java语言规范声明:
否则,我们说有默认值 访问权限,仅在以下时允许 访问发生在 声明类型的包。
我理解它的方式,该字段在同一个编译单元中声明和访问,因此在同一个包中,因此应该可以访问。
更奇怪的是,从? extends Test
向Test
添加向下转换会使该字段可见,即以下代码编译:
public class Test {
String s;
void foo(Object o) {
Test t = getClass().cast(o);
String os = t.s;
}
}
我是偶然发现了编译器错误,还是误解了Java规范?
修改 我现在在另一台电脑上。在这里,javac接受代码,但eclipse仍然没有。这台机器上的版本:
Eclipse平台
版本:3.4.2构建ID: M20090211-1700
JDK 1.6.0
编辑2 实际上,javac接受了代码。我通过运行ant build来测试,它使用IBM的Ascpect J编译器......
答案 0 :(得分:6)
试试这个:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[编辑澄清]:
getClass().cast(o)
会返回“capture#1-of? extends Test
”类型的对象,而不是Test
。所以这个问题与泛型以及编译器如何处理它有关。我不知道泛型规范的细节,但考虑到一些编译器(这里的评论)确实接受了你的代码,那么这可能是规范中的一个循环漏洞,或者这些编译器中的一些并不完全符合规范。 / p>
[最后的想法]:
我相信eclipse编译器实际上(小心)正确。对象o
实际上可能是Test的扩展(并在另一个包中定义),编译器无法知道是否确实如此。所以它将它视为另一个包中定义的扩展实例的最坏情况。如果向类final
添加Test
限定符可以允许访问字段s
,那么这将是非常正确的,但事实并非如此。
答案 1 :(得分:4)
foo()
,因此无法保证s
可见。例如,添加
protected void bar() {
foo();
}
然后在另一个包
中的某个子类Banana
中
public void quux() { bar(); }
和哎呀! getClass()
产生Banana
,无法看到s
。
编辑:从某种意义上说,other.package.Banana 没有字段s
。如果Banana 在同一个包中,它仍然可以拥有自己的s
属性,并且必须通过{{1}引用Test
的{{1}} }}
答案 2 :(得分:2)
我无法重现你所说的话。这些都可以在没有任何警告,错误或任何直接使用javac的情况下为我编译。
WinXP,javac 1.6.0_16
不,我尝试使用eclipse(v3.4.1,Build id:M20080911-1700),第一个使用eclipse:
The field Test.s is not visible
至少对于编译器合规性级别1.6和1.5。
有趣的是,如果您查看快速修复选项,它会列出Change to 's'
分辨率。这当然不能解决问题。因此,eclipse编译器和Quick-fix“生成器”似乎对此也有不同的看法; - )
对于第一个我得到的eclipse中的编译器合规性级别1.4(正如预期的那样)
s cannot be resolved or is not a field
我得到的第二个
Type mismatch: cannot convert from Object to Test
如果我直接在命令行中指定-source 1.4
和target -1.4
javac
说明第一个
cannot find symbol
我得到的第二个
incompatible types
答案 3 :(得分:1)
实际上几乎在所有情况下,除非Generics要求,否则使用Java强制转换操作符会更好(也更安全)。我讨论了它here。 Java强制转换操作符看起来很冗长,但它是正确的工具。
使用运算符替换cast
方法在Eclipse中编译得很好。
public class Test {
String s;
void foo(Object o) {
String os = ((Test) o).s;
}
}
答案 4 :(得分:0)
非常奇怪。由于未知原因(对我而言),eclipse编译器需要显式转换:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
虽然没有使用Sun的JDK(我在GNU / Linux上运行的是1.6.0_16版本),代码完美编译。