web-dev-qa-db-fra.com

Classe intérieure en interface vs en classe

Quelle est la différence entre ces deux déclarations de classe intérieure? Commenter également les avantages/inconvénients?

cas A: classe dans une classe.

public class Levels {   
  static public class Items {
    public String value;
    public String path;

    public String getValue() {
      return value;}
  }
}

et cas B: classe dans l'interface.

public interface Levels{

  public class Items {
    public String value;
    public String path;

    public String getValue() {
      return value;}
  }
}

Correction faite: au placement de la méthode getvalue.

informations complémentaires: Je peux instancier la classe Items dans les deux cas A et B dans une autre classe qui n'implémente pas l'interface AT ALL.

public class Z{//NOTE: NO INTERFACE IMPLEMENTED here!!!!
 Levels.Items items = new Levels.Items();
}

Étant donné qu’une interface n’est pas instanciée, tous les éléments d’une interface sont accessibles par notation point sans interface LEVELS instanciée simplement parce qu’il est impossible d’instancier une interface, ce qui permet de rendre une classe définie dans une interface perméable aux références statiques.

Donc, dire que la classe Items dans le cas B n’est pas statique n’a aucun sens. Puisque les deux cas A et B sont instanciés de la même manière, je ne cherche pas de sémantique sur ce qui est statique ou intérieur ou imbriqué. Arrêtez de me donner des réponses sur la sémantique. Je veux les différences/avantages du compilateur, du runtime et du comportement, ou si aucun ne le dit, alors. Pas plus de réponses sur la sémantique s'il vous plaît !!!!! Un expert en spécification JVM ou .NET VM répondra à cette question plutôt qu’à une simple sémantique.

28
Blessed Geek

Les classes internes statiques sont généralement similaires aux classes de niveau supérieur, sauf que la classe interne a accès à toutes les variables static et aux méthodes de la classe englobante. Le nom de la classe englobante est effectivement ajouté à l'espace de nom du paquet de la classe interne. En déclarant une classe en tant que classe interne statique, vous indiquez que la classe est liée de manière indissociable au contexte de la classe englobante.

Les classes internes non statiques sont moins courantes. La principale différence est que les instances d'une classe interne non statique contiennent une référence implicite à une instance de la classe englobante et ont ainsi accès aux variables et méthodes d'instance de cette instance de classe englobante. Cela conduit à des idiomes d’instanciation étranges, par exemple:

Levels levels = new Levels(); // first need an instance of the enclosing class

// The items object contains an implicit reference to the levels object
Levels.Items items  = levels.new Items(); 

Les classes internes non statiques sont beaucoup plus intimement liées à leurs classes englobantes que les classes internes statiques. Ils ont des utilisations valables (par exemple, les itérateurs sont souvent implémentés en tant que classes internes non statiques dans la classe de la structure de données sur laquelle ils sont itérés).

C'est une erreur courante de déclarer une classe interne non statique alors que vous n'avez vraiment besoin que du comportement de classe interne statique. 

14
Henry

Une classe interne static est une classe imbriquée et la classe non statique est appelée une classe interne. Pour plus, regardez ici .

Cependant, j'aime bien citer un extrait du même lien.

Une classe imbriquée statique interagit avec les membres d'instance de son extérieur classe (et autres classes) juste comme toute autre classe de haut niveau. En effet, une classe imbriquée statique est comportementalement une classe de niveau supérieur qui a été imbriquée dans une autre classe de niveau supérieur pour la commodité de l'emballage.

Vous n'avez pas utilisé Word static dans le deuxième cas. Et vous pensez que ce serait implicitement static car c’est une interface. Vous avez raison de supposer cela.

Vous pouvez instancier la classe interne dans votre interface, tout comme une classe imbriquée statique, car il s'agit en réalité d'une classe imbriquée static.

Levels.Items hello = new Levels.Items();

Ainsi, la déclaration ci-dessus sera valide dans vos deux cas. Votre premier cas est une classe imbriquée statique, et dans le second cas, vous n'avez pas spécifié static, mais même dans ce cas, il s'agirait d'une classe imbriquée statique car elle figure dans l'interface. Par conséquent, aucune différence autre que le fait que l'une soit imbriquée dans une classe et l'autre dans une interface.

Normalement, une classe interne dans une classe, not in interface, serait instanciée comme ci-dessous.

Levels levels = new Levels();
Levels.Items items = levels.new Items();

De plus, une classe interne "non statique" aura une référence implicite à sa classe externe. Ce n'est pas le cas avec la classe imbriquée "statique".

27
Adeel Ansari

Si vous déclarez une classe imbriquée dans une interface, elle est toujours public et static . Alors:

public interface Levels{
    class Items {
        public String value;
        public String path;

        public String getValue() {return value;}
    }
}

Est exactement la même chose que 

public interface Levels{
    public static class Items {
        public String value;
        public String path;

        public String getValue() {return value;}
    }
}

Et même

public interface Levels{
    static class Items {
        public String value;
        public String path;

        public String getValue() {return value;}
    }
}

J'ai vérifié cela avec javap -verbose et ils produisent tous

Compiled from "Levels.Java"
public class Levels$Items extends Java.lang.Object
  SourceFile: "Levels.Java"
  InnerClass: 
   public #14= #3 of #23; //Items=class Levels$Items of class Levels
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method   #4.#21; //  Java/lang/Object."<init>":()V
const #2 = Field    #3.#22; //  Levels$Items.value:Ljava/lang/String;
const #3 = class    #24;    //  Levels$Items
const #4 = class    #25;    //  Java/lang/Object
const #5 = Asciz    value;
const #6 = Asciz    Ljava/lang/String;;
const #7 = Asciz    path;
const #8 = Asciz    <init>;
const #9 = Asciz    ()V;
const #10 = Asciz   Code;
const #11 = Asciz   LineNumberTable;
const #12 = Asciz   LocalVariableTable;
const #13 = Asciz   this;
const #14 = Asciz   Items;
const #15 = Asciz   InnerClasses;
const #16 = Asciz   LLevels$Items;;
const #17 = Asciz   getValue;
const #18 = Asciz   ()Ljava/lang/String;;
const #19 = Asciz   SourceFile;
const #20 = Asciz   Levels.Java;
const #21 = NameAndType #8:#9;//  "<init>":()V
const #22 = NameAndType #5:#6;//  value:Ljava/lang/String;
const #23 = class   #26;    //  Levels
const #24 = Asciz   Levels$Items;
const #25 = Asciz   Java/lang/Object;
const #26 = Asciz   Levels;

{
public Java.lang.String value;

public Java.lang.String path;

public Levels$Items();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method Java/lang/Object."<init>":()V
   4:   return
  LineNumberTable: 
   line 2: 0

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      5      0    this       LLevels$Items;


public Java.lang.String getValue();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   getfield    #2; //Field value:Ljava/lang/String;
   4:   areturn
  LineNumberTable: 
   line 7: 0

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      5      0    this       LLevels$Items;


}

Les exemples que vous donnez des classes imbriquées/internes sont de mauvais exemples (IMO). De plus, le 2ème exemple n'est pas valide en Java car une interface ne peut déclarer (implicitement) que des méthodes abstraites. Voici un meilleur exemple:

public interface Worker {

    public class Response {
        private final Status status;
        private final String message;
        public Response(Status status, String message) {
            this.status = status; this.message = message;
        }
        public Status getStatus() { return status; }
        public String getMessage() { return message; }
    }

    ...

    public Response doSomeOperation(...);
}

En intégrant la classe Response, nous indiquons qu'il s'agit d'un élément fondamental de l'API de travailleur sans autre utilisation.

La classe Map.Entry est un exemple bien connu de cet idiome.

7
Stephen C

IMHO, avantage est que vous avez moins de classes encombrant votre dossier de projet si elles sont triviales; Le désavantage est que lorsque votre classe interne grandit avec le changement d’exigences, le maintenacne devient votre cauchemar. 

0
Ricky

Je pensais que le premier déclarerait une classe Niveaux et une classe interne statique appelée Items. Les éléments pourraient être référencés par Levels.Items et seraient statiques.

Tandis que le second déclarerait une classe interne simple, à laquelle on peut accéder en utilisant Levels.Items, comme ci-dessous:

Levels.Items hello = new Levels.Items();

EDIT: c'est totalement faux, lisez les commentaires et autres réponses.

0
BlueTrin