47 votes

Avertissement d'Eclipse concernant l'accesseur synthétique pour les classes privées statiques imbriquées en Java ?

Mon collègue a suggéré de rendre plus rigoureux plusieurs des paramètres de formatage de code et d'avertissement d'Eclipse. La majorité de ces changements sont logiques, mais je reçois cet avertissement bizarre en Java. Voici un code de test pour reproduire le "problème" :

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass
    {
        public void doSomething() {}
    }

    final private InnerClass anInstance;

    {
        this.anInstance = new InnerClass();   // !!!
        this.anInstance.doSomething();
    }
}
// using "this.anInstance" instead of "anInstance" prevents another warning,
// Unqualified access to the field WeirdInnerClassJavaWarning.anInstance

La ligne avec le ! !! me donne cet avertissement dans Eclipse avec mes nouveaux paramètres d'avertissement :

Accès au constructeur englobant WeirdInnerClassJavaWarning.InnerClass() est émulé par un accesseur synthétique synthétique. Augmenter sa visibilité améliorera améliorera vos performances.

Qu'est-ce que cela signifie ? L'avertissement disparaît lorsque je remplace "private static class" par "protected static class", ce qui n'a aucun sens pour moi.


éditer : J'ai finalement trouvé la "bonne" solution. Le vrai problème ici semble être que cette classe privée statique imbriquée n'a pas de constructeur public. Cette simple modification a permis de supprimer l'avertissement :

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass
    {
        public void doSomething() {}
        public InnerClass() {}
    }

    final private InnerClass anInstance;

    {
        this.anInstance = new InnerClass();
        this.anInstance.doSomething();
    }
}

Je veux que la classe soit une classe privée imbriquée (de sorte qu'aucune autre classe ne puisse y avoir accès, y compris les sous-classes de la classe englobante) et je veux qu'elle soit une classe statique.

Je ne comprends toujours pas pourquoi le fait de rendre la classe imbriquée protégée plutôt que privée est une autre méthode pour résoudre le "problème", mais peut-être est-ce une bizarrerie d'Eclipse.

(mes excuses, j'aurais dû l'appeler NestedClass au lieu de InnerClass pour être plus clair).

43voto

Eddie Points 27755

Vous pouvez vous débarrasser de cet avertissement comme suit :

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass {
        protected InnerClass() {}  // This constructor makes the warning go away
        public void doSomething() {}
    }

    final private InnerClass anInstance;
    {
        this.anInstance = new InnerClass(); 
        this.anInstance.doSomething();
    }
}

Comme d'autres l'ont dit, Eclipse se plaint parce qu'une classe privée sans constructeur explicite ne peut pas être instanciée de l'extérieur, sauf via la méthode synthétique que le compilateur Java crée. Si vous prenez votre code, que vous le compilez, puis que vous le décompilez avec jad (*), vous obtenez le résultat suivant (reformaté) :

public class Test {
  private static class InnerClass {
    public void doSomething() {}
    // DEFAULT CONSTRUCTOR GENERATED BY COMPILER:
    private InnerClass() {}

    // SYNTHETIC METHOD GENERATED BY THE JAVA COMPILER:    
    InnerClass(InnerClass innerclass) {
      this();
    }
  }

  public Test() {
    anInstance.doSomething();
  }

  // Your instance initialization as modified by the compiler:
  private final InnerClass anInstance = new InnerClass(null);
}

Si vous ajoutez un constructeur protégé, le code synthétique est inutile. Le code synthétique est théoriquement, je suppose, plus lent d'une quantité mineure que le code non synthétique utilisant un constructeur public ou protégé.

(*) Pour jad, j'ai fait un lien vers une page Wikipedia ... le domaine qui hébergeait ce programme a expiré, mais Wikipedia fait un lien vers un autre que je n'ai pas testé moi-même. Je sais qu'il existe d'autres décompilateurs (peut-être plus récents), mais c'est celui que j'ai commencé à utiliser. Note : Il se plaint lors de la décompilation de fichiers de classe Java récents, mais il fait quand même un bon travail.

21voto

robinst Points 9249

À propos, le paramètre permettant de désactiver l'avertissement se trouve dans la page des erreurs et avertissements de Java, sous "Style de code", et s'intitule :

Accès à un membre non accessible d'un type englobant

9voto

alamar Points 6376

Vous ne pouvez pas instancier une InnerClass à partir de WeirdInnerClassJavaWarning. C'est privé, la JVM ne vous le permettrait pas, mais le langage Java (pour une raison quelconque) le ferait.

Par conséquent, javac créerait une méthode supplémentaire dans InnerClass qui renverrait simplement new InnerClass(), vous permettant ainsi de créer des instances InnerClass à partir de WeirdInnerClassJavaWarning.

Je ne pense pas qu'il faille vraiment s'en débarrasser car la baisse de performance serait minime. Cependant, vous pouvez le faire si vous le voulez vraiment.

3voto

Carlos Heuberger Points 11804

Je ne comprends toujours pas pourquoi le fait de rendre la classe imbriquée protégée plutôt que privée est une autre méthode pour résoudre le "problème", mais il s'agit peut-être d'une bizarrerie d'Eclipse.

Il ne s'agit pas d'une bizarrerie ou d'un bug d'Eclipse, mais d'une caractéristique de Java. Le site Spécification du langage Java, 8.8.9 dit :

... si la classe est déclarée protégée, alors le constructeur par défaut reçoit implicitement le modificateur d'accès protected ...

2voto

Joseph Lust Points 4961

Pour aider les gens, voici ce que vous obtenez si vous utilisez le code de classe original dans la question avec

javac -XD-printflat WeirdInnerClassJavaWarning.java -d tmp

Sortie brute, le compilateur a ajouté les commentaires. Notez l'ajout de la classe privée et du constructeur du paquet synthétique.

public class WeirdInnerClassJavaWarning {
    {
    }

    public WeirdInnerClassJavaWarning() {
        super();
    }
    {
    }
    private final WeirdInnerClassJavaWarning$InnerClass anInstance;
    {
        this.anInstance = new WeirdInnerClassJavaWarning$InnerClass(null);
        this.anInstance.doSomething();
    }
}

class WeirdInnerClassJavaWarning$InnerClass {

    /*synthetic*/ WeirdInnerClassJavaWarning$InnerClass(WeirdInnerClassJavaWarning$1 x0) {
        this();
    }

    private WeirdInnerClassJavaWarning$InnerClass() {
        super();
    }

    public void doSomething() {
    }
}

/*synthetic*/ class WeirdInnerClassJavaWarning$1 {
}

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