代码之家  ›  专栏  ›  技术社区  ›  meriton

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

  •  7
  • meriton  · 技术社区  · 15 年前

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

    public class Test {
    
        String s;
    
        void foo(Object o) {
            String os = getClass().cast(o).s;
        }
    }
    

    只有在以下情况下才允许访问: 访问发生在 在其中声明类型的包。

    按照我的理解,字段是在同一个编译单元中声明和访问的,因此在同一个包中,因此应该可以访问。

    更奇怪的是,这让我感到沮丧 ? 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

    编辑2 事实上,javac接受代码。我已经通过运行ant构建进行了测试,它使用IBM的Aspect J编译器。。。

    5 回复  |  直到 15 年前
        1
  •  6
  •   alphazero    15 年前

    试试这个:

    void foo(Object o) {
        Test foo = getClass().cast(o);
        String so = foo.s;
    }
    

    getClass().cast(o) capture#1-of? extends Test Test . 因此,这个问题与泛型以及编译器如何处理它有关。我不知道泛型规范的细节,但考虑到一些编译器(此处按注释)确实接受您的代码,那么这要么是规范中的循环漏洞,要么这些编译器中的一些并不完全符合规范。

    我相信eclipse编译器实际上(仔细地)是正确的。对象 o 实际上可能是测试的扩展(并在另一个包中定义),编译器无法知道是否确实如此。因此,它将其视为另一个包中定义的扩展实例的最坏情况。如果添加一个 final 类的限定符 会允许进入现场的 s ,但事实并非如此。

        2
  •  4
  •   Jonathan Feinberg    15 年前

    好吧,让我想想。我得说编译器不能很好地保证这一点 foo() 将由包中的某个实体调用,因此不能保证 s 是可见的。例如,添加

    protected void bar() {
        foo();
    }
    

    然后在某个子类中 Banana 在另一个包裹里

    public void quux() { bar(); }
    

    哎呀! getClass() 香蕉 ,看不见 s .

    田地 . 如果香蕉 在相同的包中,它仍然可以有自己的包 s Test s 通过 super .

        3
  •  2
  •   jitter    15 年前

    WinXP,JavaC1.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编译器和快速修复“生成器”似乎对此也有不同的看法;-)


    对于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
    
        4
  •  1
  •   Community Dai    7 年前

    实际上,在几乎所有情况下,除非泛型需要,否则使用JavaCast操作符更好(更安全)。我讨论过了 here

    替换 cast 带有运算符的方法在Eclipse中编译得很好。

    public class Test {
    
        String s;
    
        void foo(Object o) {
            String os = ((Test) o).s;
        }
    }
    

    alphazero 是正确的 here 那次月食是过于谨慎了。

        5
  •  0
  •   Pascal Thivent    15 年前

    很奇怪。出于未知原因(对我来说),eclipse编译器需要显式强制转换:

    void foo(Object o) {
        String os = ((Test)getClass().cast(o)).s;
    }
    

    而代码在没有使用Sun的JDK强制转换的情况下可以完美地编译(我在GNU/Linux上运行的是1.6.0_16版)。