Question Classe interne Java et classe imbriquée statique


Quelle est la principale différence entre une classe interne et une classe imbriquée statique en Java? Le design / la mise en œuvre jouent-ils un rôle dans le choix de l'un d'entre eux?


1464
2017-09-16 08:22


origine


Réponses:


Du Tutoriel Java:

Les classes imbriquées sont divisées en deux catégories: statique et non statique. Les classes imbriquées déclarées statiques sont simplement appelées des classes imbriquées statiques. Les classes imbriquées non statiques sont appelées classes internes.

Les classes imbriquées statiques sont accessibles en utilisant le nom de la classe englobante:

OuterClass.StaticNestedClass

Par exemple, pour créer un objet pour la classe imbriquée statique, utilisez cette syntaxe:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Les objets qui sont des instances d'une classe interne existent dans une instance de la classe externe. Considérez les classes suivantes:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

Une instance de InnerClass peut exister uniquement dans une instance de OuterClass et a un accès direct aux méthodes et champs de son instance englobante.

Pour instancier une classe interne, vous devez d'abord instancier la classe externe. Ensuite, créez l'objet interne dans l'objet externe avec cette syntaxe:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

voir: Tutoriel Java - Classes imbriquées

Pour l'exhaustivité, notez qu'il existe aussi une chose telle qu'une classe interne sans pour autant une instance englobante:

class A {
  int t() { return 1; }
  static A a =  new A() { int t() { return 2; } };
}

Ici, new A() { ... } est un classe interne définie dans un contexte statique et n'a pas d'instance englobante.


1480
2017-09-16 08:28



le Tutoriel Java dit:

Terminologie: les classes imbriquées sont   divisé en deux catégories: statique   et non statique. Classes imbriquées   sont déclarés statiques sont simplement appelés   classes imbriquées statiques. Non statique   les classes imbriquées sont appelées inner   Des classes.

Dans le langage courant, les termes "imbriqué" et "interne" sont utilisés de manière interchangeable par la plupart des programmeurs, mais j'utiliserai le terme correct "classe imbriquée" qui couvre à la fois l'intérieur et le statique.

Les classes peuvent être imbriquées À l'infini, par exemple. la classe A peut contenir la classe B qui contient la classe C qui contient la classe D, etc. Cependant, plus d'un niveau d'imbrication de classe est rare, car c'est généralement un mauvais design.

Il y a trois raisons pour lesquelles vous pourriez créer une classe imbriquée:

  • organisation: parfois il semble plus judicieux de trier une classe dans l'espace de noms d'une autre classe, surtout quand elle ne sera pas utilisée dans un autre contexte
  • access: les classes imbriquées ont un accès spécial aux variables / champs de leurs classes contenant (précisément quelles variables / champs dépendent du type de classe imbriquée, qu'elle soit interne ou statique).
  • la commodité: avoir à créer un nouveau fichier pour chaque nouveau type est gênant, encore une fois, surtout quand le type ne sera utilisé que dans un contexte

Il y a quatre types de classe imbriquée en Java. En bref, ils sont:

  • classe statique: déclaré comme membre statique d'une autre classe
  • classe interne: déclaré en tant que membre d'instance d'une autre classe
  • classe interne locale: déclaré dans une méthode d'instance d'une autre classe
  • classe interne anonyme: comme une classe interne locale, mais écrite comme une expression qui renvoie un objet unique

Laissez-moi élaborer en plus de détails.


Classes statiques

Les classes statiques sont les plus faciles à comprendre car elles n'ont rien à voir avec les instances de la classe conteneur.

Une classe statique est une classe déclarée en tant que membre statique d'une autre classe. Tout comme les autres membres statiques, une telle classe est vraiment juste un hangar qui utilise la classe contenant comme espace de nom, par exemple. la classe Chèvre déclaré comme membre statique de la classe Rhinocéros dans le paquet Pizza est connu par le nom pizza.Rhino.Goat.

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

Franchement, les classes statiques sont une fonctionnalité assez sans valeur car les classes sont déjà divisées en espaces de noms par paquets. La seule vraie raison concevable de créer une classe statique est qu'une telle classe a accès aux membres statiques privés de sa classe contenant, mais je trouve que c'est une justification assez boiteuse pour que la fonction de classe statique existe.


Classes intérieures

Une classe interne est une classe déclarée en tant que membre non statique d'une autre classe:

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

    private void jerry() {
        Goat g = new Goat();
    }
}

Comme avec une classe statique, la classe interne est qualifiée de qualifiée par son nom de classe contenant, pizza.Rhino.Goat, mais à l'intérieur de la classe contenant, il peut être connu par son nom simple. Cependant, chaque instance d'une classe interne est liée à une instance particulière de sa classe conteneur: Chèvre créé en jerry, est implicitement lié à Rhinocéros exemple ce dans jerry. Sinon, nous faisons les associés Rhinocéros instance explicite lorsque nous instancions Chèvre:

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(Notez que vous vous référez au type interne comme juste Chèvre dans l'étrange Nouveau syntaxe: Java déduit le type contenant du rhinocéros partie. Et oui nouveau rhino.Goat () aurait eu plus de sens pour moi aussi.)

Alors qu'est-ce que cela nous apporte? Eh bien, l'instance de classe interne a accès aux membres d'instance de l'instance de classe contenant. Ces membres d'instance englobants sont référencés dans la classe interne via juste leurs noms simples, pas via  ce (ce dans la classe interne fait référence à l'instance de classe interne, pas à l'instance de classe contenant associée):

public class Rhino {

    private String barry;

    public class Goat {
        public void colin() {
            System.out.println(barry);
        }
    }
}

Dans la classe interne, vous pouvez vous référer à ce de la classe contenant comme Rhino.thiset vous pouvez utiliser ce pour se référer à ses membres, par exemple. Rhino.this.barry.


Classes locales

Une classe interne locale est une classe déclarée dans le corps d'une méthode. Une telle classe n'est connue que dans sa méthode conteneur, elle ne peut donc être instanciée et ses membres sont accédés dans sa méthode conteneur. Le gain est qu'une instance de classe interne locale est liée à et peut accéder aux variables locales finales de sa méthode conteneur. Lorsque l'instance utilise un dernier local de sa méthode conteneur, la variable conserve la valeur qu'elle détenait au moment de la création de l'instance, même si la variable est hors de portée (c'est effectivement la version brute et limitée des fermetures de Java).

Une classe interne locale n'étant pas le membre d'une classe ou d'un package, elle n'est pas déclarée avec un niveau d'accès. (Soyez clair, cependant, que ses propres membres ont des niveaux d'accès comme dans une classe normale.)

Si une classe interne locale est déclarée dans une méthode d'instance, une instanciation de la classe interne est liée à l'instance détenue par la méthode conteneur. ce au moment de la création de l'instance, les membres de l'instance de la classe contenant sont accessibles comme dans une classe interne d'instance. Une classe interne locale est instanciée simplement via son nom, par exemple. classe interne locale Chat est instancié comme nouveau chat (), pas nouveau this.Cat () comme vous pouvez vous y attendre.


Classes internes anonymes

Une classe interne anonyme est une manière syntaxiquement pratique d'écrire une classe interne locale. Le plus souvent, une classe interne locale est instanciée au plus juste une fois chaque fois que sa méthode conteneur est exécutée. Ce serait bien, si nous pouvions combiner la définition de la classe interne locale et son unique instanciation en une forme de syntaxe pratique, et ce serait également bien si nous n'avions pas besoin d'imaginer un nom pour la classe (le moins utile les noms de votre code contient, mieux c'est). Une classe interne anonyme permet ces deux choses:

new *ParentClassName*(*constructorArgs*) {*members*}

Ceci est une expression retournant une nouvelle instance d'une classe sans nom qui étend ParentClassName. Vous ne pouvez pas fournir votre propre constructeur; plutôt, on est implicitement fourni qui appelle simplement le super constructeur, donc les arguments fournis doivent correspondre au super constructeur. (Si le parent contient plusieurs constructeurs, le "plus simple" est appelé, "le plus simple" comme déterminé par un ensemble plutôt complexe de règles ne valant pas la peine d'apprendre en détail - faites attention à ce que NetBeans ou Eclipse vous disent.)

Vous pouvez également spécifier une interface à implémenter:

new *InterfaceName*() {*members*}

Une telle déclaration crée une nouvelle instance d'une classe sans nom qui étend Object et implémente InterfaceName. Encore une fois, vous ne pouvez pas fournir votre propre constructeur; dans ce cas, Java fournit implicitement un constructeur sans-argument, do-nothing (donc il n'y aura jamais d'arguments constructeur dans ce cas).

Même si vous ne pouvez pas donner un constructeur à une classe interne anonyme, vous pouvez toujours faire n'importe quelle configuration en utilisant un bloc d'initialisation (un bloc {} placé en dehors de toute méthode).

Soyez clair qu'une classe interne anonyme est simplement une manière moins flexible de créer une classe interne locale avec une instance. Si vous voulez une classe interne locale qui implémente plusieurs interfaces ou qui implémente des interfaces tout en étendant une classe autre que Objet ou qui spécifie son propre constructeur, vous êtes bloqué en créant une classe interne locale nommée régulière.


524
2017-09-16 09:24



Je ne pense pas que la vraie différence est devenue claire dans les réponses ci-dessus.

Premier à obtenir les termes corrects:

  • Une classe imbriquée est une classe contenue dans une autre classe au niveau du code source.
  • Il est statique si vous le déclarez avec statique modificateur.
  • Une classe imbriquée non statique est appelée classe interne. (Je reste avec une classe imbriquée non statique.)

La réponse de Martin est juste pour le moment. Cependant, la vraie question est la suivante: quel est le but de déclarer une classe imbriquée statique ou non?

Tu utilises classes imbriquées statiques si vous voulez juste garder vos classes ensemble si elles appartiennent localement ensemble ou si la classe imbriquée est exclusivement utilisée dans la classe englobante. Il n'y a pas de différence sémantique entre une classe imbriquée statique et toutes les autres classes.

Classes imbriquées non statiques sont une bête différente. Semblables à des classes internes anonymes, ces classes imbriquées sont en réalité des fermetures. Cela signifie qu'ils capturent leur portée environnante et leur instance englobante et la rendent accessible. Peut-être qu'un exemple éclaircira cela. Voir ce talon d'un conteneur:

public class Container {
    public class Item{
        Object data;
        public Container getContainer(){
            return Container.this;
        }
        public Item(Object data) {
            super();
            this.data = data;
        }

    }

    public static Item create(Object data){
        // does not compile since no instance of Container is available
        return new Item(data);
    }
    public Item createSubItem(Object data){
        // compiles, since 'this' Container is available
        return new Item(data);
    }
}

Dans ce cas, vous voulez avoir une référence d'un élément enfant au conteneur parent. En utilisant une classe imbriquée non statique, cela fonctionne sans travail. Vous pouvez accéder à l'instance englobante de Container avec la syntaxe Container.this.

Plus d'explications hardcore suivantes:

Si vous regardez les bytecodes Java que le compilateur génère pour une classe imbriquée (non statique), cela peut devenir encore plus clair:

// class version 49.0 (49)
// access flags 33
public class Container$Item {

  // compiled from: Container.java
  // access flags 1
  public INNERCLASS Container$Item Container Item

  // access flags 0
  Object data

  // access flags 4112
  final Container this$0

  // access flags 1
  public getContainer() : Container
   L0
    LINENUMBER 7 L0
    ALOAD 0: this
    GETFIELD Container$Item.this$0 : Container
    ARETURN
   L1
    LOCALVARIABLE this Container$Item L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 1
  public <init>(Container,Object) : void
   L0
    LINENUMBER 12 L0
    ALOAD 0: this
    ALOAD 1
    PUTFIELD Container$Item.this$0 : Container
   L1
    LINENUMBER 10 L1
    ALOAD 0: this
    INVOKESPECIAL Object.<init>() : void
   L2
    LINENUMBER 11 L2
    ALOAD 0: this
    ALOAD 2: data
    PUTFIELD Container$Item.data : Object
    RETURN
   L3
    LOCALVARIABLE this Container$Item L0 L3 0
    LOCALVARIABLE data Object L0 L3 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

Comme vous pouvez le voir, le compilateur crée un champ caché Container this$0. Ceci est défini dans le constructeur qui a un paramètre supplémentaire de type Container pour spécifier l'instance englobante. Vous ne pouvez pas voir ce paramètre dans la source mais le compilateur le génère implicitement pour une classe imbriquée.

L'exemple de Martin

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

serait donc compilé à un appel de quelque chose comme (en bytecodes)

new InnerClass(outerObject)

Par souci d'exhaustivité:

Une classe anonyme est un exemple parfait d'une classe imbriquée non statique qui n'a aucun nom associé et ne peut pas être référencée plus tard.


124
2017-09-16 09:12



Je pense qu'aucune des réponses ci-dessus ne vous explique la différence réelle entre une classe imbriquée et une classe imbriquée statique en termes de conception d'application:

OverView

Une classe imbriquée pourrait être non statique ou statique et dans chaque cas est une classe définie dans une autre classe. Une classe imbriquée doit exister uniquement pour servir est une classe englobante, si une classe imbriquée est utile à d'autres classes (et pas seulement à l'inclusion), elle devrait être déclarée comme une classe de premier niveau.

Différence

Classe imbriquée Nonstatic : est implicitement associé à l'instance englobante de la classe conteneur, cela signifie qu'il est possible d'invoquer des méthodes et des variables d'accès de l'instance englobante. Une utilisation courante d'une classe imbriquée non statique consiste à définir une classe Adapter.

Classe imbriquée statique : ne peut pas accéder à l'instance de classe englobante et aux méthodes invoke, il doit donc être utilisé lorsque la classe imbriquée n'a pas besoin d'accéder à une instance de la classe englobante. Une utilisation courante de la classe imbriquée statique consiste à implémenter des composants de l'objet externe.

Conclusion

La principale différence entre les deux est donc: La classe imbriquée non statique peut accéder à l'instance de la classe de conteneur, alors que la statique ne peut pas l'être..


82
2018-05-27 07:43



En termes simples, nous avons besoin de classes imbriquées principalement parce que Java ne fournit pas de fermetures.

Les classes imbriquées sont des classes définies dans le corps d'une autre classe englobante. Ils sont de deux types - statiques et non statiques.

Ils sont traités comme des membres de la classe englobante, vous pouvez donc spécifier l'un des quatre spécificateurs d'accès - private, package, protected, public. Nous n'avons pas ce luxe avec des classes de haut niveau, qui ne peuvent être déclarées public ou paquet-privé.

Classes internes aka Les classes non-stack ont ​​accès aux autres membres de la classe supérieure, même si elles sont déclarées privées alors que les classes imbriquées statiques n'ont pas accès aux autres membres de la classe supérieure.

public class OuterClass {
    public static class Inner1 {
    }
    public class Inner2 {
    }
}

Inner1 est notre classe interne statique et Inner2 est notre classe interne qui n'est pas statique. La principale différence entre eux, vous ne pouvez pas créer un Inner2 instance sans un extérieur où comme vous pouvez créer un Inner1 objet indépendamment.

Quand utiliseriez-vous la classe interne?

Pensez à une situation où Class A et Class B sont liés, Class B doit avoir accès Class A membres, et Class B est lié uniquement à Class A. Les classes intérieures entrent en scène.

Pour créer une instance de classe interne, vous devez créer une instance de votre classe externe.

OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();

ou

OuterClass.Inner2 inner = new OuterClass().new Inner2();

Quand utiliseriez-vous la classe interne statique?

Vous devez définir une classe interne statique lorsque vous savez qu'elle n'a aucune relation avec l'instance de la classe / top class englobante. Si votre classe interne n'utilise pas les méthodes ou les champs de la classe externe, c'est juste une perte d'espace, alors faites-la statique.

Par exemple, pour créer un objet pour la classe imbriquée statique, utilisez cette syntaxe:

OuterClass.Inner1 nestedObject = new OuterClass.Inner1();

L'avantage d'une classe imbriquée statique est qu'elle n'a pas besoin d'un objet de la classe ou de la classe supérieure contenant pour fonctionner. Cela peut vous aider à réduire le nombre d'objets créés par votre application lors de l'exécution.


30
2017-10-17 22:35



Je pense que la convention généralement suivie est celle-ci:

  • classe statique dans une classe de haut niveau est un classe imbriquée
  • classe non statique dans une classe de haut niveau est un classe interne, qui plus loin a deux autres formes:
    • classe locale - classes nommées déclarées à l'intérieur d'un bloc comme une méthode ou un corps de constructeur
    • classe anonyme - classes sans nom dont les instances sont créées dans des expressions et des instructions

Cependant, peu d'autres points à se souvenir sont:

  • Les classes de niveau supérieur et les classes imbriquées statiques sont sémantiquement identiques sauf que dans le cas d'une classe imbriquée statique, elle peut faire une référence statique aux champs / méthodes statiques privés de sa classe Outer [parent] et vice versa.

  • Les classes internes ont accès aux variables d'instance de l'instance englobante de la classe Outer [parent]. Cependant, toutes les classes internes n'ont pas d'instances englobantes, par exemple des classes internes dans des contextes statiques, comme une classe anonyme utilisée dans un bloc d'initialisation statique.

  • La classe anonyme par défaut étend la classe parent ou implémente l'interface parent et il n'y a pas d'autre clause pour étendre une autre classe ou implémenter d'autres interfaces. Alors,

    • new YourClass(){};veux dire class [Anonymous] extends YourClass {}
    • new YourInterface(){};   veux dire class [Anonymous] implements YourInterface {}

Je sens que la plus grande question qui reste ouverte à utiliser et quand? Cela dépend surtout du scénario auquel vous avez affaire, mais lire la réponse donnée par @jrudolph peut vous aider à prendre une décision.


24
2017-12-16 11:38



Voici les principales différences et similitudes entre la classe interne Java et la classe imbriquée statique.

J'espère que cela aide!

Classe intérieure

  • Peut accéder à la classe extérieure à la fois instance et statique méthodes et champs
  • Associé à l'instance de classe englobante donc pour instancier il faut d'abord une instance de classe externe (note Nouveau lieu de mot-clé):

    Outerclass.InnerClass innerObject = outerObject.new Innerclass();
    
  • Ne peux pas définir tout membres statiques se

  • Ne peux pas avoir Classe ou Interface déclaration

Classe imbriquée statique

  • Ne peut pas accéder classe extérieure exemple méthodes ou champs

  • Non associé à une instance de classe englobante Donc, pour l'instancier:

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
    

Similitudes

  • Tous les deux Cours intérieurs peut accéder même champs privés et méthodes de classe extérieure
  • Également Classe extérieure avoir accès à champs privés et méthodes de classes internes
  • Les deux classes peuvent avoir un modificateur d'accès privé, protégé ou public

Pourquoi utiliser des classes imbriquées?

Selon la documentation d'Oracle, il y a plusieurs raisons (documentation complète):

  • C'est un moyen de regrouper logiquement des classes qui ne sont utilisées que dans un seul endroit: Si une classe n'est utile qu'à une autre classe, il est logique de l'intégrer dans cette classe et de garder les deux ensemble. L'imbrication de ces "classes auxiliaires" rend leur paquet plus simple.

  • Cela augmente l'encapsulation: Considérons deux classes de niveau supérieur, A et B, où B doit avoir accès aux membres de A qui seraient autrement déclarés privés. En masquant la classe B dans la classe A, les membres A peuvent être déclarés privés et B peut y accéder. En outre, B lui-même peut être caché du monde extérieur.

  • Il peut conduire à un code plus lisible et maintenable: L'imbrication de petites classes dans des classes de niveau supérieur place le code plus près de l'endroit où il est utilisé.


23
2017-12-31 20:53



Classe imbriquée: classe à l'intérieur de la classe

Les types:

  1. Classe imbriquée statique
  2. Classe imbriquée non statique [Classe interne]

Différence:

Classe imbriquée non statique [Classe interne]

Dans l'objet de classe imbriqué non statique de la classe interne existe dans l'objet de la classe externe. Donc, ce membre de données de la classe externe est accessible à la classe interne. Donc, pour créer un objet de classe interne, nous devons d'abord créer un objet de classe externe.

outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass(); 

Classe imbriquée statique

Dans l'objet de classe imbriqué statique de classe interne n'a pas besoin d'objet de classe externe, parce que le mot "statique" n'indique pas besoin de créer l'objet.

class outerclass A {
    static class nestedclass B {
        static int x = 10;
    }
}

Si vous voulez accéder à x, alors écrivez la méthode intérieure suivante

  outerclass.nestedclass.x;  i.e. System.out.prinltn( outerclass.nestedclass.x);

13
2018-04-04 10:14