JDK 7中关于私有成员访问的javac行为变化

14

这段代码在使用JDK版本1.6.0_33-b03-424的javac编译器时可以正常编译,但在使用JDK版本1.7.0_06的javac编译器时无法编译。

public class Test {
    private final int i = 0;

    void test(Object o) {
        if (getClass().isInstance(o)) {
            System.out.println(getClass().cast(o).i);
        }
    }
}

javac 的输出为:

Test.java:6: error: i in Test is defined in an inaccessible class or interface
        System.out.println(getClass().cast(o).i);
                                             ^
1 error

将代码更改为在临时变量中存储getClass.cast()的结果可以使程序编译通过。

虽然这很容易解决,但我找不到JLS 7中对此更改的任何理由,也没有在JDK 7发行说明中提到过这样的更改。虽然有一处提到了涉及通用类型参数的私有成员的访问更改,但这并不适用于这里。

这是javac中的一个退化吗? 它是否正在强制实施之前没有执行的限制?


看起来确实是一个回归问题,我无法理解JDK7的错误信息有任何意义。 - Joachim Isaksson
Eclipse编译器(JDT 3.7.2)也无法编译此代码...但是却给出了相当奇怪的快速修复建议“用i替换i”。 - CurtainDog
这段代码在我的Mac上编译正常,运行的是jdk 1.7.0_05-b05版本,很难相信他们在_06版本中引入了这个问题,但我会升级并查看发生了什么。 - JoeG
使用Java 6版本的NetBeans在代码编辑器中显示相同的错误,但如果我忽略这个错误,它确实允许我编译(甚至运行)代码。 - madth3
1个回答

7

我对此感到困惑,唯一的解释可能是两个因素的结合。

1_ getClass() 文档中写道:

实际结果类型为 Class<? extends |X|>,其中 |X| 是 在其上调用 getClass 的表达式的静态类型的擦除。

2_ 在Java 7中引入的不兼容之一编译器不再允许访问类型变量的私有成员

因此,编译器无法确定类型转换是基类或子类,并阻止访问私有成员,因为即使在原始父类中定义,如果将类型转换分配给子类,也将是非法的,如下例所示:

class BaseTest {
    private final int i = 1;

    void test(Object o) {
        if (getClass().isInstance(o)) {                
            TestAccess to = TestAccess.class.cast(o);
            //System.out.println(to.i);  // ERROR: i has private access in BaseTest
        }
    }
}

class TestAccess extends BaseTest{}

所以,我猜这是Java的另一个怪癖,由于规则在更复杂的例子中更有意义。

有趣的是,我以为 X.getClass() 返回的是 Class<X>,但它不返回也说得通。将代码更改为显式转换 getClass() 的结果也可以解决问题,这似乎证实了这个解释。不过,错误消息完全不同有点奇怪。 - Phil Shapiro
在这种情况下,类Test不是final的,方法test()也不是private的。因此,子类可以调用Test::test,然后getClass()将返回子类的类对象。然后,从一个实例转换为该子类,您无法访问私有字段I。 - davidbak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接