java编译器奇怪之处:在同一个类中声明了字段,但;“不可见”;
eclipse编译器拒绝编译以下代码,表示字段s不可见。(IBM的Aspect J编译器也拒绝了,称“s无法解析”)这是为什么java编译器奇怪之处:在同一个类中声明了字段,但;“不可见”;,java,generics,compiler-construction,compiler-errors,Java,Generics,Compiler Construction,Compiler Errors,eclipse编译器拒绝编译以下代码,表示字段s不可见。(IBM的Aspect J编译器也拒绝了,称“s无法解析”)这是为什么 public class Test { String s; void foo(Object o) { String os = getClass().cast(o).s; } } Java语言规范规定: 否则,我们说存在违约 只有在以下情况下才允许访问: 访问发生在 在其中声明类型的包 按照我的理解,字段是在同一个编译单元中声
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
Java语言规范规定:
否则,我们说存在违约
只有在以下情况下才允许访问:
访问发生在
在其中声明类型的包
按照我的理解,字段是在同一个编译单元中声明和访问的,因此在同一个包中,因此应该可以访问
更奇怪的是,在中添加了一个downcast?将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
JDK1.6.0
编辑2
事实上,javac接受代码。我已经通过运行ant构建进行了测试,它使用IBM的Aspect J编译器…我无法重现您所说的内容。对于我来说,这两种语言都可以很好地编译,没有任何警告、错误或任何直接使用javac的东西 WinXP,JavaC1.6.0_16
不,我试过eclipse(v3.4.1,构建id:M20080911-1700),第一次它说:
The field Test.s is not visible
至少符合编译器1.6和1.5级的要求。
有趣的是,如果您查看快速修复选项,它会列出一个更改为“s”
分辨率。这当然不能解决问题。因此,eclipse编译器和快速修复“生成器”似乎对此也有不同的看法;-)
对于eclipse中的编译器遵从性级别1.4(正如预期的那样),我得到了第一个
s cannot be resolved or is not a field
Type mismatch: cannot convert from Object to Test
incompatible types
第二次我得到了
s cannot be resolved or is not a field
Type mismatch: cannot convert from Object to Test
incompatible types
如果我直接在命令行中指定
-source 1.4
和target-1.4
,则javac
表示第一个
cannot find symbol
第二次我得到了
s cannot be resolved or is not a field
Type mismatch: cannot convert from Object to Test
incompatible types
好吧,让我想想。我想说,编译器不能正确地保证包中的某个实体将调用
foo()
,因此不能保证s
是可见的。例如,添加
protected void bar() {
foo();
}
然后在另一个包中的某个子类中
哎呀getClass()
生成Banana
,它不能看到s
编辑:从某种意义上说,other.package.Banana没有字段s
。如果香蕉在同一个包中,它可能仍然有自己的s
属性,并且必须通过super
引用Test
的s
,尝试以下操作:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[编辑以澄清]:
getClass().cast(o)
返回类型为“capture”的对象?扩展测试,而不是测试。因此,这个问题与泛型以及编译器如何处理它有关。我不知道泛型规范的细节,但考虑到一些编译器(此处按注释)确实接受您的代码,那么这要么是规范中的循环漏洞,要么这些编译器中的一些并不完全符合规范
[最后的想法]:
我相信eclipse编译器实际上(仔细地)是正确的。对象o
实际上可能是测试的扩展(并在另一个包中定义),编译器无法知道是否确实是这样。因此,它将其视为另一个包中定义的扩展实例的最坏情况。如果在类测试中添加final
限定符将允许访问字段s
,这将是非常正确的,但事实并非如此。实际上,在几乎所有情况下,除非泛型需要,否则使用Java cast操作符更好(更安全)。我讨论过了。JavaCast操作符确实查看了详细信息,但它是这里的正确工具
用操作符替换cast
方法在Eclipse中编译得很好
public class Test {
String s;
void foo(Object o) {
String os = ((Test) o).s;
}
}
我认为这是正确的,日食过于谨慎。非常奇怪。出于未知原因(对我来说),eclipse编译器需要显式强制转换:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
虽然代码在没有使用Sun的JDK强制转换的情况下可以完美编译(我在GNU/Linux上运行的是1.6.0_16版)。kewl问题。Josh Bloch在哪里?我现在在另一台计算机上,javac接受代码,但eclipse仍然不接受。你的日食说明了什么?明天我将在原始机器上重复测试,以确保测试正确。我的理解是:香蕉看不到s意味着编译单元中的代码香蕉无法访问s。但是我的代码在编译单元测试中…是的,您的代码在编译单元测试中,但假定的子类可能不是,该类将是getClass()的结果,并且该类无法看到包保护字段。我认为编译器错误是正确的。注意上面的代码是在MacOSX/Eclipse3.4.Erm上编译的。。。我已经做了?或者从我的第二个代码片段中重命名局部变量可以实现什么?我知道这很有效。我的问题是为什么第一个代码段没有,因为您假设编译器也在同一个包中定义了泛型捕获。如果不是,那么包级别的可见性将不再适用。(对不起,我浏览了你的文章,错过了结尾。)