eclipse编译器拒绝编译以下代码,提示字段s不可见。 (IBM的Aspect J编译器也拒绝,指出“s无法解析”)这是为什么?
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 版本号: M20090211-1700
JDK 1.6.0
编辑2 事实上,javac 接受该代码。我通过运行 ant 构建进行了测试,它使用 IBM 的 Ascpect J 编译器......
试试这个:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[编辑以澄清]:
getClass().cast(o)
返回类型为 'capture#1-of? extends Test
' 的对象,而不是 Test
。 所以这个问题与泛型以及编译器如何处理它有关。 我不知道泛型规范的详细信息,但鉴于某些编译器(根据此处的评论)确实接受您的代码,那么这要么是规范中的漏洞,要么其中一些编译器不完全符合规范。
[最后的想法]: 我相信 eclipse 编译器在这里实际上(仔细地)是正确的。 对象
o
实际上可能是 Test 的扩展(并在另一个包中定义),并且编译器无法知道情况是否确实如此。 因此,它将其视为另一个包中定义的扩展实例的最坏情况。 如果向类 final
添加 Test
限定符将允许访问字段 s
,那将是非常正确的,但事实并非如此。
好吧,让我们看看。我想说编译器无法正确保证
foo()
将被包内的某些实体调用,因此无法保证 s
可见。例如,添加
protected void bar() {
foo();
}
然后在某个子类中
Banana
在另一个包中
public void quux() { bar(); }
哎呀!
getClass()
产生 Banana
,它看不到 s
。
编辑:从某种意义上说,other.package.Banana 没有 have 字段
s
。如果 Banana 在同一个包中,它仍然可以拥有自己的 s
属性,并且必须通过 Test
引用 s
的 super
。
我无法重现你所说的内容。这些都对我来说编译得很好,没有警告、错误或直接使用 javac 的任何东西。
WinXP、javac 1.6.0_16
不,我尝试使用 eclipse(v3.4.1,构建 ID:M20080911-1700),第一个它说:
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
很奇怪。由于未知原因(对我来说),Eclipse 编译器需要显式强制转换:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
虽然代码可以完美编译,无需使用 Sun 的 JDK 进行强制转换(我在 GNU/Linux 上运行版本 1.6.0_16)。