升级到 Java 7 后,泛型类的类型参数化字段变得不可见

Posted

技术标签:

【中文标题】升级到 Java 7 后,泛型类的类型参数化字段变得不可见【英文标题】:Type-parameterized field of a generic class becomes invisible after upgrading to Java 7 【发布时间】:2011-12-04 21:53:27 【问题描述】:

现在 Eclipse Indigo SR1 和内置 Java 7 支持在一两周后终于推出,我正在将我的游乐场项目从 Helios SR2 + JDK 1.6_23 迁移到 Indigo SR1 + JDK 1.7.0。在完全重建所有项目后,只有一个类无法编译。以下类在 Java 1.6(和 1.5)上编译并运行得非常好:

public abstract class Area<A extends Area<?>> implements Comparable<Area<?>> 

    private String name;
    private Area<?> parent;
    private Set<A> areas;

    protected Area(String name, A... areas) 
        this.name = name;
        this.areas = new TreeSet<A>();
        for (A area : areas) 
            area.parent = this;
            this.areas.add(area);
        
    

    public Set<A> getAreas() 
        return areas;
    

    // ...

area.parent = this; 行失败,parent 出现以下错误:

字段Area.parent 不可见

在首先怀疑 Eclipse 编译器之后,我尝试使用 JDK 1.7.0 中的普通 javac,但它给出了基本相同的错误,而 JDK 1.6.0_23 中的 javac 成功且没有错误。

将可见性更改为protected 或默认值可以解决问题。但是为什么完全超出了我的理解。我在http://bugs.sun.com 上四处张望,但找不到任何类似的报告。

修复编译错误的另一种方法是将类中所有使用的A 声明替换为Area&lt;?&gt;(或完全删除):

public abstract class Area<A extends Area<?>> implements Comparable<Area<?>> 

    private String name;
    private Area<?> parent;
    private Set<Area<?>> areas;

    protected Area(String name, Area<?>... areas) 
        this.name = name;
        this.areas = new TreeSet<Area<?>>();
        for (Area<?> area : areas) 
            area.parent = this;
            this.areas.add(area);
        
    

    public Set<Area<?>> getAreas() 
        return areas;
    

    // ...

但这破坏了吸气剂的目的。例如以下类:

public class Country extends Area<City> 

    public Country(String name, City... cities) 
        super(name, cities);
    


我希望它返回 Set&lt;City&gt;,而不是 Set&lt;Area&lt;?&gt;&gt;

Java 7 中的哪些变化导致这些类型参数化的字段变得不可见?

【问题讨论】:

不应该,但是将 ctor 从 protected 更改为 public 重要吗? @DaveNewton:不,没关系。 【参考方案1】:

这似乎是 javac6 的错误,已在 javac7 中修复。

解决方法是向上转换:

((Area<?>)area).parent = this;

这看起来很奇怪——为什么我们需要一个向上转换来访问超类中的成员?

根本问题是,私有成员被明确排除在继承之外,因此A 没有parent 成员。同样的问题可以用一个非泛型的例子来证明。

错误消息“parent 在区域中具有私有访问权限”并不十分准确,但在大多数情况下可能没问题。但是在这种情况下,它会产生误导,更好的消息是“A 不会从 Area 继承私有成员 'parent'


为了调查的兴趣,让我们根据JLS对您的示例进行全面分析:

§4.4:类型变量 X 绑定 T &amp; I1 ... In 的成员是交集类型 (§4.9) T &amp; I1 ... In 的成员,出现在声明类型变量的位置。

§4.9:交集类型与具有空主体的类类型(§8)具有相同的成员,直接超类Ck和直接超接口IT1 , ..., ITn,在与交集类型相同的包中声明出现。

§6.6.1:如果成员或构造函数被声明为私有,则当且仅当它发生在包含成员声明的***类(§7.6)的主体中时才允许访问或构造函数。

§8.2:声明为私有的类的成员不会被该类的子类继承。

§8.5:类从其直接超类和直接超接口继承超类和超接口的所有非私有成员类型,这些类型都可以被类中的代码访问,并且不会被类中的声明隐藏。

【讨论】:

有趣。谢谢你的指点。是否有任何已知的错误报告可以确认 javac6 错误? 我不知道。在我看来,javac7 改进了很多东西;他们可能彻底检查了整个类型系统代码,因为 javac6 太混乱了,无法修复。在那种情况下,他们无法获得完整的 javac6 错误列表。 使用新的关键字,终于找到了两个相关的bug报告:6711619和7022052。

以上是关于升级到 Java 7 后,泛型类的类型参数化字段变得不可见的主要内容,如果未能解决你的问题,请参考以下文章

01泛型是什么?——《Android打怪升级之旅》

Java泛型

将实例化的 System.Type 作为泛型类的类型参数传递

Java泛型

java泛型——泛型类泛型方法泛型接口

第六节:Java泛型