java编译器奇怪之处:在同一个类中声明了字段,但;“不可见”;

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语言规范规定: 否则,我们说存在违约 只有在以下情况下才允许访问: 访问发生在 在其中声明类型的包 按照我的理解,字段是在同一个编译单元中声

eclipse编译器拒绝编译以下代码,表示字段s不可见。(IBM的Aspect J编译器也拒绝了,称“s无法解析”)这是为什么

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上编译的。。。我已经做了?或者从我的第二个代码片段中重命名局部变量可以实现什么?我知道这很有效。我的问题是为什么第一个代码段没有,因为您假设编译器也在同一个包中定义了泛型捕获。如果不是,那么包级别的可见性将不再适用。(对不起,我浏览了你的文章,错过了结尾。)