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

发布于 2024-12-09 12:56:03 字数 2445 浏览 2 评论 0 原文

现在 Eclipse Indigo SR1 内置 Java 7 支持在一两周后终于结束了,我正在迁移我的 Playground 项目从 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 (或将其完全删除):

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,而不是 Set>

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

Now Eclipse Indigo SR1 with builtin Java 7 support is finally out since a week or two, I'm migrating my playground projects from Helios SR2 + JDK 1.6_23 to Indigo SR1 + JDK 1.7.0. After a full rebuild of all projects, only one class has failed to compile. It's the following class which compiles and runs perfectly fine on Java 1.6 (and 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;
    }

    // ...
}

The line area.parent = this; fails with the following error on parent:

The field Area<capture#1-of ?>.parent is not visible

After first suspecting the Eclipse compiler, I tried with plain javac from JDK 1.7.0, but it gives basically the same error whereas the javac from JDK 1.6.0_23 succeeds without errors.

Changing the visibility to protected or default solves the problem. But the why is completely beyond me. I peeked around on http://bugs.sun.com, but I couldn't find any similar report.

Another way to fix the compilation error is to replace all used A declarations inside the class by Area<?> (or to remove it altogether):

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;
    }

    // ...
}

But this breaks the purpose of the getter. In case of for example the following class:

public class Country extends Area<City> {

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

}

I'd expect it to return Set<City>, not Set<Area<?>>.

Which change in Java 7 has caused those type-parameterized fields to become invisible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

金兰素衣 2024-12-16 12:56:03

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

解决方法是通过向上转换:

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

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

根本问题是,私有成员被明确排除在继承之外,因此 A 没有 parent 成员。可以通过非通用示例来演示相同的问题。

错误消息“parent has private access in Area”不太准确,但在大多数情况下可能没问题。然而在这种情况下,这是误导性的,更好的消息是“A 不会从 Area 继承私有成员“父级”


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

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

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

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

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

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

This appears to be a javac6 bug, which is fixed in javac7.

The workaround is by up-cast:

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

Which seems really odd - why would we need an up-cast to access a member in super class?

The root problem is, private members are specifically excluded from inheritance, therefore A does not have a parent member. The same problem can be demonstrated by a non-generic example.

The error message "parent has private access in Area" is not quite accurate, though it's probably fine for most cases. However in this case, it's misleading, a better message would be "A does not inherit the private member 'parent' from Area"


For the interest of investigation, let's do a full analysis on your example based on the JLS:

  • §4.4: The members of a type variable X with bound T & I1 ... In are the members of the intersection type (§4.9) T & I1 ... In appearing at the point where the type variable is declared.

  • §4.9: The intersection type has the same members as a class type (§8) with an empty body, direct superclass Ck and direct superinterfaces IT1 , ..., ITn, declared in the same package in which the intersection type appears.

  • §6.6.1: If the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.

  • §8.2: Members of a class that are declared private are not inherited by subclasses of that class.

  • §8.5: A class inherits from its direct superclass and direct superinterfaces all the non-private member types of the superclass and superinterfaces that are both accessible to code in the class and not hidden by a declaration in the class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文