web-dev-qa-db-fra.com

Pourquoi ne pouvons-nous pas avoir une méthode statique dans une classe interne (non statique)?

Pourquoi ne pouvons-nous pas avoir une méthode statique dans une classe interne non statique? 

Si je rends la classe interne statique, cela fonctionne. Pourquoi ?

136
Rahul Garg

Etant donné qu'une instance d'une classe interne est associée implicitement à une instance de sa classe externe, elle ne peut définir aucune méthode statique. Etant donné qu'une classe imbriquée statique ne peut pas faire directement référence à des variables d'instance ou à des méthodes définies dans sa classe englobante, elle ne peut les utiliser que via une référence d'objet, il est donc prudent de déclarer des méthodes statiques dans une classe imbriquée statique.

105
Bill the Lizard

Il n'y a pas grand intérêt à autoriser une méthode statique dans une classe interne non statique; comment y accéderais-tu? Vous ne pouvez pas accéder (au moins initialement) à une instance de classe interne non statique sans passer par une instance de classe externe. Il n'y a pas de moyen purement statique de créer une classe interne non statique.

Pour une classe externe Outer, vous pouvez accéder à une méthode statique test() comme ceci:

Outer.test();

Pour une classe interne statique Inner, vous pouvez accéder à sa méthode statique innerTest() comme ceci:

Outer.Inner.innerTest();

Cependant, si Inner n'est pas statique, il n'y a maintenant aucun moyen purement statique de référencer la méthode innertest. Les classes internes non statiques sont liées à une instance spécifique de leur classe externe. Une fonction est différente d'une constante, en ce sens qu'une référence à Outer.Inner.CONSTANT est garantie d'être non ambiguë, de sorte qu'un appel de fonction Outer.Inner.staticFunction(); ne l'est pas. Supposons que vous avez Inner.staticFunction() qui appelle getState(), défini dans Outer. Si vous essayez d'appeler cette fonction statique, vous avez maintenant une référence ambiguë à la classe Inner. Autrement dit, sur quelle instance de la classe interne appelez-vous la fonction statique? Cela compte. Vous voyez, il n’existe pas de méthode réellement statique pour référencer cette méthode statique, en raison de la référence implicite à l’objet externe.

Paul Bellora a raison de dire que les concepteurs de langage auraient pu permettre cela. Ils devraient alors soigneusement interdire tout accès à la référence implicite à la classe externe dans les méthodes statiques de la classe interne non statique. À ce stade, quelle est la valeur de cette classe interne si vous ne pouvez pas référencer la classe externe, sauf de manière statique? Et si l'accès statique est correct, pourquoi ne pas déclarer toute la classe interne statique? Si vous rendez simplement la classe interne elle-même statique, vous n'avez alors aucune référence implicite à la classe externe et vous n'avez plus cette ambiguïté.

Si vous utilisez besoin méthodes statiques sur une classe interne non statique, vous devrez probablement repenser votre conception.

44
Eddie

J'ai une théorie qui peut être ou ne pas être correcte.

Tout d'abord, vous devez savoir certaines choses sur la manière dont les classes internes sont implémentées en Java. Supposons que vous ayez ce cours:

class Outer {
    private int foo = 0;
    class Inner implements Runnable {
        public void run(){ foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(); }
}

Lorsque vous le compilerez, le bytecode généré ressemblera à quelque chose comme ceci:

class Outer {
    private int foo = 0;
    static class Inner implements Runnable {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public void run(){ this$0.foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(this); }
}

Maintenant, si nous autorisons les méthodes statiques dans des classes internes non statiques, vous voudrez peut-être faire quelque chose comme ceci.

class Outer {
    private int foo = 0;
    class Inner {
        public static void incrFoo(){ foo++; }
    }
}

... qui semble assez raisonnable, car la classe Inner semble avoir une incarnation par Outer instance. Mais comme nous l'avons vu ci-dessus, les classes internes non statiques ne sont en réalité que du sucre syntaxique pour les classes "internes" statiques. Le dernier exemple serait donc à peu près équivalent à:

class Outer {
    private int foo = 0;
    static class Inner {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public static void incrFoo(){ this$0.foo++; }
    }
}

... qui ne fonctionnera manifestement pas, puisque this$0 est non statique. Cela explique en partie pourquoi les méthodes statiques ne sont pas autorisées (bien que vous puissiez indiquer que vous pouvez autoriser des méthodes statiques tant qu'elles ne font pas référence à l'objet englobant), et pourquoi vous ne pouvez pas avoir de champs statiques non finaux il serait contre-intuitif si des instances de classes internes non statiques provenant d'objets différents partageaient un "état statique"). Cela explique également pourquoi les champs finaux sont autorisés (tant qu'ils ne font pas référence à l'objet englobant).

20
gustafc

La seule raison est "pas indispensable", alors pourquoi prendre la peine de le soutenir?

Syntaxiquement, il n’ya aucune raison d’interdire à une classe interne d’avoir des membres statiques. Bien qu'une instance de Inner soit associée à une instance de Outer, il est toujours possible d'utiliser Outer.Inner.myStatic pour faire référence à un membre statique de Inner si Java le décide.

Si vous avez besoin de partager quelque chose entre toutes les instances de Inner, vous pouvez simplement les mettre dans Outer en tant que membres statiques. Ce n'est pas pire que d'utiliser des membres statiques dans Inner, où Outer peut toujours accéder à un membre privé de Inner de toute façon (n'améliore pas l'encapsulation).

Si vous avez besoin de partager quelque chose parmi toutes les instances de Inner créées par un objet outer, il est plus logique de les placer dans la classe Outer en tant que membres ordinaires.

Je ne suis pas d'accord avec l'opinion selon laquelle "une classe imbriquée statique est à peu près juste une classe de niveau supérieur". Je pense qu'il vaut mieux considérer une classe/classe interne imbriquée statique comme faisant partie de la classe externe, car ils peuvent accéder aux membres privés de la classe externe. Et les membres de la classe externe sont également des "membres de la classe interne". Il n'est donc pas nécessaire de prendre en charge le membre statique dans la classe interne. Un membre ordinaire/statique dans une classe externe suffira.

6
Don Li

Réponse courte: le modèle mental que la plupart des programmeurs ont de la manière dont la portée fonctionne n'est pas le modèle utilisé par javac. La mise en correspondance du modèle plus intuitif aurait nécessité un grand changement dans le fonctionnement de javac.

La raison principale pour laquelle les membres statiques des classes internes sont souhaitables est la propreté du code: un membre statique utilisé uniquement par une classe interne doit y vivre, plutôt que d'être placé dans la classe externe. Considérer:

class Outer {
   int outID;

   class Inner {
      static int nextID;
      int id = nextID++;

      String getID() {
         return outID + ":" + id;
      }
   }
}

Considérez ce qui se passe dans getID () lorsque j'utilise l'identifiant non qualifié "outID". La portée dans laquelle cet identificateur apparaît ressemble à quelque chose comme:

Outer -> Inner -> getID()

Ici encore, comme javac fonctionne ainsi, le niveau "extérieur" de la portée inclut à la fois les membres statiques et les membres d'instance de l'extérieur. Ceci est déroutant car on nous dit généralement de considérer la partie statique d'une classe comme un autre niveau de la portée:

Outer static -> Outer instance -> instanceMethod()
         \----> staticMethod()

Dans cette optique, staticMethod () ne peut évidemment voir que les membres statiques de Outer. Mais si tel était le cas, le référencement d’une variable d’instance dans une méthode statique entraînerait l’erreur "le nom ne peut pas être résolu". En réalité, le nom se trouve dans la portée, mais un niveau supplémentaire de vérification intervient et détermine que le nom a été déclaré dans un contexte d'instance et est référencé à partir d'un contexte statique.

OK, comment cela se rapporte-t-il aux classes internes? Naïvement, nous pensons qu'il n'y a aucune raison pour que les classes internes ne puissent pas avoir une portée statique, car nous imaginons qu'elle fonctionne comme suit:

Outer static -> Outer instance -> Inner instance -> getID()
         \------ Inner static ------^

En d'autres termes, les déclarations statiques dans la classe interne et les déclarations d'instance dans la classe externe ont toutes les deux une portée dans le contexte d'instance de la classe interne, mais aucune n'est en réalité imbriquée dans l'autre; les deux sont plutôt imbriqués dans la portée statique d'Outer.

Cela ne correspond tout simplement pas à la manière dont javac fonctionne: il existe un seul niveau de portée pour les membres statiques et par instance, et la portée est toujours strictement imbriquée. Même l'héritage est implémenté en copiant les déclarations dans la sous-classe plutôt qu'en se ramifiant et en recherchant la portée de la superclasse.

Pour prendre en charge les membres statiques des classes internes, javac devrait soit scinder les portées statique et instance et prendre en charge les ramifications et les hiérarchies de portées, ou encore étendre son idée booléenne de "contexte statique" afin de changer le type de contexte à tous les niveaux de la classe imbriquée dans la portée actuelle.

3
Russell Zahniser

Pourquoi ne pouvons-nous pas avoir une méthode statique dans une classe interne non statique?

Remarque: Une classe imbriquée non statique est appelée classe interne, vous n'avez donc pas non-static inner class en tant que tel.

Une instance de classe interne n'a pas d'existence sans une instance correspondante de classe externe. Une classe interne ne peut pas déclarer de membres statiques autres que les constantes de temps de compilation. Si cela avait été autorisé, la signification de static aurait été ambiguë. Dans ce cas, il y aurait eu certaines confusions:

  1. Cela signifie-t-il qu'il n'y a qu'une seule instance dans VM?
  2. Ou seulement une instance par objet externe?

C'est pourquoi les concepteurs ont probablement pris la décision de ne pas traiter ce problème du tout.

Si je rends la classe interne statique, cela fonctionne. Pourquoi ?

De nouveau, vous ne pouvez pas rendre une classe interne statique. Vous pouvez plutôt déclarer une classe statique imbriquée. Dans ce cas, cette classe imbriquée fait en réalité partie de la classe externe et peut avoir des membres statiques sans aucun problème.

3
i_am_zero

Ce sujet a attiré l'attention de nombreuses personnes, mais j'essaierai encore de l'expliquer dans les termes les plus simples.

Premièrement, en référence à http://docs.Oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , une classe ou une interface est initialisée immédiatement avant la première occurrence/invocation d'un membre précédé du mot clé static. 

  1. Donc, si nous mettons en place un membre statique dans une classe interne, cela conduira à l'initialisation de la classe interne, pas nécessairement de la classe externe/englobante. Donc, nous gênons la séquence d'initialisation de la classe.

  2. Prenez également en compte le fait qu'une classe interne non statique est associée à l'instance d'une classe englobante/externe. Ainsi, s’associer à une instance signifiera que la classe interne existera dans une instance de classe Outer et sera différente d’une instance à l’autre. 

Pour simplifier le point, afin d'accéder au membre statique, nous avons besoin d'une instance d'une classe externe, à partir de laquelle nous devrons créer une instance d'une classe interne non statique. Les membres statiques ne sont pas censés être liés à des instances et vous recevez donc une erreur de compilation. 

3
Doomed93

Une classe interne est quelque chose de complètement différent d'une classe imbriquée statique, bien que leur syntaxe soit semblable. Les classes imbriquées statiques ne sont qu'un moyen de regroupement, alors que les classes internes ont une association forte - et un accès à toutes les valeurs de - leur classe externe. Vous devez être sûr de pourquoi vous voulez utiliser une classe interne et ensuite, le choix de la classe que vous utilisez doit être assez naturel. Si vous avez besoin de déclarer une méthode statique, c'est probablement une classe imbriquée statique que vous voulez quand même.

2
B.E.

De: https://docs.Oracle.com/javase/tutorial/Java/javaOO/nested.html

Comme pour les méthodes et les variables d'instance, une classe interne est associée à une instance de sa classe englobante et dispose d'un accès direct aux méthodes et aux champs de cet objet. De plus, étant donné qu'une classe interne est associée à une instance, elle ne peut définir aucun membre statique.

L'explication d'Oracle est superficielle et délicate. Comme il n'y a aucune raison technique ou syntaxique de préempter des membres statiques au sein d'une classe interne (cela est autorisé dans d'autres langages tels que C #), la motivation des concepteurs Java était probablement le goût conceptuel et/ou une question de commodité technique.

Voici ma spéculation:

Contrairement aux classes de niveau supérieur, les classes internes dépendent d'une instance: une instance de classe interne est associée à une instance de chacune de ses classes externes et a un accès direct à leurs membres. C'est la principale motivation pour les avoir en Java. En d'autres termes: une classe interne est destinée à une instanciation dans le contexte d'une instance de classe externe. Sans instance de classe externe, une classe interne ne devrait pas être plus utilisable que les autres membres d'instance de la classe externe. Appelons cela le esprit dépendant de l'instance des classes internes.

La nature même des membres statiques (qui ne sont pas orientés objet) entre en conflit avec l'esprit dépendant de l'instance des classes internes (qui IS est orienté objet) car vous pouvez référencer/appeler un membre statique. d'une classe interne sans instance de classe externe à l'aide du nom de classe interne qualifié.

Les variables statiques en particulier peuvent offenser d'une autre manière encore: deux instances d'une classe interne associées à différentes instances de la classe externe partageraient des variables statiques. Étant donné que les variables sont un composant d'état, les deux instances de classe internes partageraient en fait l'état indépendamment des instances de classe externes auxquelles elles sont associées. Ce n'est pas inacceptable que les variables statiques fonctionnent de cette façon (nous les acceptons en Java comme un compromis raisonnable pour la pureté OOP), mais il est sans doute plus grave que de les autoriser dans des classes internes dont les instances sont déjà couplées. avec des instances de classe externes par conception. Interdire les membres statiques au sein des classes internes en faveur de esprit dépendant de l'instance procure l'avantage supplémentaire de préempter cette infraction plus profonde OOP.

En revanche, aucune infraction de ce type n’est entraînée par des constantes statiques, qui ne constituent pas véritablement un état et sont donc admissibles. Pourquoi ne pas interdire les constantes statiques pour une cohérence maximale avec esprit dépendant de l'instance? Peut-être parce que les constantes n’ont pas besoin d’occuper plus de mémoire que nécessaire (si elles sont forcées d’être non statiques, elles sont copiées dans toutes les instances de classe internes, ce qui est potentiellement inutile). Sinon, je ne peux pas imaginer la raison de cette exception.

Ce n’est peut-être pas un raisonnement solide, mais à l’OMI, cela donne le sens de la remarque superficielle d’Oracle à ce sujet.

2

Tout d'abord, pourquoi quelqu'un veut-il définir le membre statique dans une classe interne non statique? answer is, pour que le membre de classe externe puisse utiliser les membres statiques avec le nom de classe interne uniquement, non?

Mais dans ce cas, nous pouvons définir directement le membre dans la classe externe. qui sera associé à tout objet de classe interne, dans l'instance de classe externe.

comme ci-dessous le code,

public class Outer {

  class Inner {

    public static void method() {

    }

  }

}

peut être écrit comme ça 

public class Outer {

  void method() {

   }

   class Inner {


  }

}

Donc, à mon avis, pour ne pas compliquer le code, le concepteur Java n'autorise pas cette fonctionnalité ou cette fonctionnalité pourrait apparaître dans les versions futures avec certaines fonctionnalités supplémentaires. 

0
Vishnu Saini

Essayez de traiter la classe comme un champ normal, alors vous comprendrez. 

//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something 
0
user1888955

supposons qu'il y ait deux instances de classe externe et que toutes les deux aient une classe interne instanciée.Maintenant, si la classe interne a un membre statique, elle ne conservera qu'une copie de ce membre dans la zone de segment de mémoire. Dans ce cas, les deux objets de la classe externe feront référence à cet élément. une seule copie et ils peuvent l'altérer ensemble. Cela peut entraîner une situation de "lecture sale" et éviter ainsi que Java applique cette restriction. Un autre point fort pour soutenir cet argument est que Java autorise les membres statiques finaux ici, ceux dont les valeurs ne peuvent pas être changé de l'un des objets de la classe externe ..__ S'il vous plaît laissez-moi si je me trompe.

0
Malay