100 votes

Que fait « someObject.new » en Java ?

En Java, j’ai viens de découvrir que le code suivant est légal :

Pour info, le récepteur est simplement une classe d’assistance avec la signature suivante :

Je n’ai jamais vu la `` avant la notation. Comment ça marche ? Est-il possible de coder que plus conventionnellement ?

120voto

Ian Roberts Points 59836

C'est la façon d'instancier un non-statique à l'intérieur de la classe à partir de l'extérieur de la classe conteneur corps, comme décrit dans l' Oracle docs.

Chaque intérieur instance de classe est associée à une instance de sa classe conteneur. Lorsque vous new intérieure de la classe à partir de l'intérieur de sa classe contenante il utilise l' this instance du conteneur par défaut:

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      // this is the val belonging to our containing instance
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar(); // equivalent of this.new Bar()
  }
}

Mais si vous voulez créer une instance de Bar à l'extérieur de Toto ou de l'associer à une nouvelle instance avec un contenant instance autre que this alors vous devez utiliser le préfixe de la notation.

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal(); // prints 5

18voto

Mikhail Points 2585

Jetez un oeil à cet exemple:

public class Test {

    class TestInner{

    }

    public TestInner method(){
        return new TestInner();
    }

    public static void main(String[] args) throws Exception{
        Test t = new Test();
        Test.TestInner ti = t.new TestInner();
    }
}

À l'aide de javap nous pouvons afficher instructions générées pour ce code

La méthode main:

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   new     #2; //class Test
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   new     #4; //class Test$TestInner
   11:  dup
   12:  aload_1
   13:  dup
   14:  invokevirtual   #5; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  pop
   18:  invokespecial   #6; //Method Test$TestInner."<init>":(LTest;)V
   21:  astore_2
   22:  return
}

Intérieure constructeur de la classe:

Test$TestInner(Test);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LTest;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

}

Tout est simple - lors de l'invocation de TestInner constructeur, java passe instance de Test en tant que premier argument principal:12. Ne regardant pas que TestInner doit avoir un constructeur sans argument. TestInner à son tour sauve juste référence à l'objet parent, Test$TestInner:2. Lorsque vous appelez intérieure constructeur de la classe à partir d'une méthode d'instance, la référence à l'objet parent est passe automatiquement, donc vous n'avez pas à spécifier. En fait sa passe à chaque fois, mais lors de l'appel de l'extérieur, il doit être transmis de manière explicite.

t.new TestInner(); - n'est qu'un moyen pour spécifier la première caché argument TestInner constructeur, pas un type

méthode() est égale à:

public TestInner method(){
    return this.new TestInner();
}

TestInner est égale à:

class TestInner{
    private Test this$0;

    TestInner(Test parent){
        this.this$0 = parent;
    }
}

7voto

James Holderness Points 15849

Lorsque les classes internes ont été ajoutés à Java dans la version 1.1 de la langue qu'ils ont été initialement définie comme une transformation à 1,0 code compatible. Si vous regardez un exemple de cette transformation, je pense que ça va le rendre beaucoup plus claire de la façon dont un à l'intérieur de la classe fonctionne réellement.

Considérez le code de Ian Roberts réponse:

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar();
  }
}

Lorsqu'il est transformé à 1.0 compatible code, que l'intérieur de la classe Bar deviendrait quelque chose comme ceci:

class Foo$Bar {
  private Foo this$0;

  Foo$Bar(Foo outerThis) {
    this.this$0 = outerThis;
  }

  public void printVal() {
    System.out.println(this$0.val);
  }
}

L'intérieur de la classe le nom est préfixé avec l'extérieur nom de la classe de manière à le rendre unique. Caché privé this$0 membre est ajouté que détient une copie de l'extérieur, this. Et un caché constructeur est créé à l'initialisation de ce membre.

Et si vous regardez l' createBar méthode, il serait transformé en quelque chose comme ceci:

public Foo$Bar createBar() {
  return new Foo$Bar(this);
}

Donc, nous allons voir ce qui se passe lorsque vous exécutez le code suivant.

Foo f = new Foo(5);
Foo.Bar b = f.createBar();                               
b.printVal();

Nous avons d'abord instancier une instance de Foo et initialiser l' val membre à 5 (c'est à dire f.val = 5).

Ensuite, nous appelons f.createBar(), qui instancie une instance d' Foo$Bar et initialise l' this$0 membre de la valeur de this passée de createBar (c - b.this$0 = f).

Enfin, nous appelons b.printVal() qui essaie d'imprimer b.this$0.val qui f.val , qui est de 5.

Maintenant, qui était un habitué de l'instanciation d'un intérieur de classe. Regardons ce qui se passe lors de l'instanciation Bar provenant de l'extérieur de Foo.

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();

L'application de notre 1.0 transformation de nouveau, que la deuxième ligne est devenue quelque chose comme ceci:

Foo$Bar b = new Foo$Bar(f);

C'est presque identique à l' f.createBar() appel. De nouveau nous sommes de créer une instance de Foo$Bar et initialisation de l' this$0 membre de f. Encore une fois, b.this$0 = f.

Et à nouveau lorsque vous appelez b.printVal(), vous imprimez b.thi$0.val qui f.val , qui est de 5.

L'essentiel à retenir est que l'intérieur de la classe a caché membre détenant une copie de l' this à partir de l'extérieur de la classe. Lorsque vous instanciez un intérieur de classe de l'intérieur que l'extérieur de la classe, il implicitement initialisé avec la valeur courante de this. Lorsque vous instanciez l'intérieur de la classe à partir de l'extérieur de l'extérieur de la classe, vous spécifiez explicitement l'instance de l'extérieur de la classe à utiliser, via le préfixe sur l' new mot-clé.

4voto

David Z Points 49476

Pensez à `` sous forme d’un jeton unique. Un peu comme un nom de fonction avec un espace dedans.

Bien sûr, la classe n’a pas littéralement une fonction nommée , mais je devine que la syntaxe est censée suggérer que. C’est censé ressembler, vous appelez une fonction qui crée une nouvelle instance de à l’aide d’une instance particulière de pour tout accès à la classe englobante.

1voto

JavaTechnical Points 1164

L'ombrage

Si une déclaration d'un type (comme un membre de la variable ou un nom de paramètre) dans un champ particulier (comme un intérieur de classe ou une méthode de définition) a le même nom qu'une autre déclaration dans le cadre englobant, puis la déclaration des ombres de la déclaration de l'affichage de la portée. Vous ne peut pas se référer à une ombre déclaration en son nom seul. L'exemple suivant, ShadowTest, illustre cela:

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

Voici la sortie de cet exemple:

x = 23
this.x = 1
ShadowTest.this.x = 0

Cet exemple définit trois variables nommées x: La variable membre de la classe ShadowTest, la variable de membre de l'intérieur de la classe FirstLevel, et le paramètre de la méthode methodInFirstLevel. La variable x définie comme paramètre de la méthode methodInFirstLevel les ombres de la variable de l'intérieur de la classe FirstLevel. Par conséquent, lorsque vous utilisez la variable x dans la méthode methodInFirstLevel, il se réfère à la méthode de paramètre. Pour se référer à la variable membre de l'intérieur de la classe FirstLevel, utilisez le mot-clé this pour représenter le cadre englobant:

System.out.println("this.x = " + this.x);

Reportez-vous aux variables membres qui entourent les grandes étendues par le nom de la classe à laquelle ils appartiennent. Par exemple, l'instruction suivante accède à la variable membre de la classe ShadowTest de la méthode methodInFirstLevel:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

Reportez-vous à la documentation

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