45 votes

Le champ paramétré par type d'une classe générique devient invisible après la mise à niveau vers Java 7

Maintenant Eclipse Indigo SR1 avec une fonction intégrée Java 7 Le support est enfin sorti depuis une semaine ou deux, je suis en train de migrer mes projets de playground de Helios SR2 + JDK 1.6_23 vers Indigo SR1 + JDK 1.7.0. Après une reconstruction complète de tous les projets, une seule classe n'a pas réussi à compiler. Il s'agit de la classe suivante qui compile et fonctionne parfaitement bien sous Java 1.6 (et 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;
    }

    // ...
}

La ligne area.parent = this; échoue avec l'erreur suivante sur parent :

Le champ Area<capture#1-of ?>.parent n'est pas visible.

Après avoir d'abord soupçonné le compilateur Eclipse, j'ai essayé avec le simple javac à partir du JDK 1.7.0, mais il donne fondamentalement la même erreur alors que l'option javac à partir du JDK 1.6.0_23 réussit sans erreur.

Changer la visibilité en protected ou par défaut résout le problème. Mais le pourquoi me dépasse complètement. J'ai jeté un coup d'oeil sur http://bugs.sun.com mais je n'ai pas trouvé de rapport similaire.

Une autre façon de corriger l'erreur de compilation est de remplacer toutes les données utilisées par A à l'intérieur de la classe par Area<?> (ou de le supprimer complètement) :

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

    // ...
}

Mais cela brise l'objectif du getter. Dans le cas par exemple de la classe suivante :

public class Country extends Area<City> {

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

}

Je m'attendrais à ce qu'il revienne Set<City> pas Set<Area<?>> .

Quel changement dans Java 7 a fait que ces champs paramétrés par type deviennent invisibles ?

31voto

irreputable Points 25577

Cela semble être un bogue de javac6, qui est corrigé dans javac7.

La solution de contournement est l'up-cast :

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

Ce qui semble vraiment étrange - pourquoi aurions-nous besoin d'un up-cast pour accéder à un membre de la super classe ?

Le problème de Racine est que les membres privés sont spécifiquement exclus de l'héritage, donc A n'a pas de parent membre. Le même problème peut être démontré par un exemple non générique.

Le message d'erreur " le parent a un accès privé dans la zone "n'est pas tout à fait exact, bien qu'il soit probablement correct dans la plupart des cas. Cependant, dans ce cas, il est trompeur, un meilleur message serait " A n'hérite pas du membre privé 'parent' de Area "


Dans l'intérêt de l'enquête, faisons une analyse complète de votre exemple sur la base du JLS :

  • §4.4 : Les membres d'une variable de type X avec limite T & I1 ... In sont les membres du type d'intersection (§4.9) T & I1 ... In apparaissant à l'endroit où la variable de type est déclarée.

  • §4.9 : Le type d'intersection a les mêmes membres qu'un type de classe (§8) avec un corps vide, superclasse directe. Ck et des superinterfaces directes IT1 , ..., ITn, déclaré dans le même paquet dans lequel le type d'intersection apparaît.

  • §6.6.1 : Si le membre ou le constructeur est déclaré privé, l'accès est autorisé si et seulement s'il se produit dans le corps de la classe de niveau supérieur (§7.6) qui englobe la déclaration du membre ou du constructeur.

  • §8.2 : Les membres d'une classe qui sont déclarés privés ne sont pas hérités par les sous-classes de cette classe.

  • §8.5 : Une classe hérite de sa superclasse directe et de ses superinterfaces directes tous les types membres non privés de la superclasse et des superinterfaces qui sont à la fois accessibles au code de la classe et non cachés par une déclaration dans la classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X