【问题标题】:java compiler oddity: field declared in same class, yet "not visible"java编译器奇怪:在同一个类中声明的字段,但“不可见”
【发布时间】:2009-10-21 23:07:58
【问题描述】:

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

public class Test {

    String s;

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

Java 语言规范规定:

否则,我们说有默认值 访问,只有在 访问发生在 声明类型的包。

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

更奇怪的是,添加从? extends TestTest 的向下转换使该字段可见,即以下代码编译:

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 接受代码。我通过运行使用 IBM 的 Ascpect J 编译器的 ant 构建进行了测试...

【问题讨论】:

  • kewl 问题。乔什·布洛赫在哪里?

标签: java generics compiler-construction compiler-errors


【解决方案1】:

试试这个:

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

[编辑澄清]:

getClass().cast(o) 返回一个类型为“capture#1-of? extends Test”的对象,而不是Test。所以这个问题与泛型以及编译器如何处理它有关。我不知道泛型规范的详细信息,但鉴于某些编译器(此处为每个 cmets)确实接受您的代码,那么这要么是规范中的一个漏洞,要么其中一些编译器并不完全符合规范。

[最后的想法]: 我相信 eclipse 编译器实际上(仔细)在这里是正确的。 o 对象实际上可能是 Test 的扩展(并在另一个包中定义),编译器无法知道是否确实如此。因此,它将其视为在另一个包中定义的扩展实例的最坏情况。如果将final 限定符添加到类Test 将允许访问字段s,那将是非常正确的,但事实并非如此。

【讨论】:

  • 请注意以上是在 Mac OS X/Eclipse 3.4 上编译的。
  • 呃...我已经做到了?或者从我的第二个代码 sn-p 重命名局部变量会完成什么?我知道那行得通。我的问题是为什么第一个 sn-p 没有。
  • 这不是因为您假设通用捕获也由编译器在同一个包中定义。如果不是,则包级别的可见性不再适用。
  • (抱歉,我浏览了你的帖子并错过了结尾。)
【解决方案2】:

好吧,让我们看看。我想说编译器不能正确保证 foo() 将被包中的某个实体调用,因此不能保证 s 是可见的。例如,添加

protected void bar() {
    foo();
}

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

public void quux() { bar(); }

哎呀! getClass() 产生Banana,它看不到s

编辑:从某种意义上说,other.package.Banana 没有字段s。如果 Banana 在同一个包中,它仍然可以有自己的 s 属性,并且必须通过 super 引用 Tests

【讨论】:

  • 我的理解是:Banana 看不到 s 表示编译单元banana 中的代码无法访问s。但是我的代码在编译单元 Test ...
  • 是的,您的代码在编译单元 Test 中,但假定的子类可能不在,并且该类将是 getClass() 的结果,并且该类看不到包保护字段。我认为编译器错误是正确的。
【解决方案3】:

我无法重现您所说的内容。这些对我来说都编译得很好,没有警告、错误或直接使用 javac 的任何东西。

WinXP, javac 1.6.0_16


不,我尝试使用 eclipse(v3.4.1,Build 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.4target -1.4 javac 表示第一个

cannot find symbol

第二个我得到了

incompatible types

【讨论】:

  • 我现在在另一台计算机上,javac 接受代码,但 eclipse 仍然没有。你的日食说什么?明天我会在原来的机器上重复我的测试,以确保我测试正确。
【解决方案4】:

实际上在几乎所有情况下,除了泛型需要时,使用 Java 强制转换运算符会更好(也更安全)。我讨论了它here。 Java cast 运算符确实看起来很冗长,但它是这里的正确工具。

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

public class Test {

    String s;

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

我认为alphazero 是正确的here,Eclipse 只是过于谨慎了。

【讨论】:

    【解决方案5】:

    很奇怪。由于未知原因(对我而言),eclipse 编译器需要显式转换:

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

    虽然代码在没有使用 Sun JDK 的情况下完美编译(我在 GNU/Linux 上运行版本 1.6.0_16)。

    【讨论】:

      猜你喜欢
      • 2013-08-19
      • 2015-09-22
      • 2017-08-10
      • 2012-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多