Java: Class.this

112

J'ai un programme Java qui ressemble à ceci.

public class LocalScreen {

   public void onMake() {
       aFuncCall(LocalScreen.this, oneString, twoString);
   }
}

Que LocalScreen.thissignifie dans aFuncCall?

Johnny Jazz
la source

Réponses:

169

LocalScreen.thisfait référence à thisla classe englobante.

Cet exemple devrait l'expliquer:

public class LocalScreen {
    
    public void method() {
        
        new Runnable() {
            public void run() {
                // Prints "An anonymous Runnable"
                System.out.println(this.toString());
                
                // Prints "A LocalScreen object"
                System.out.println(LocalScreen.this.toString());
                
                // Won't compile! 'this' is a Runnable!
                onMake(this);
                
                // Compiles! Refers to enclosing object
                onMake(LocalScreen.this);
            }
            
            public String toString() {
                return "An anonymous Runnable!";
            }
        }.run();
    }
    
    public String toString() { return "A LocalScreen object";  }
    
    public void onMake(LocalScreen ls) { /* ... */ }
    
    public static void main(String[] args) {
        new LocalScreen().method();
    }
}

Production:

An anonymous Runnable!
A LocalScreen object

Cet article a été réécrit sous forme d'article ici .

aioobe
la source
Et si vous aviez quelque chose comme: public class a { private class a { public void run() { System.out.println(a.this.toString()); } } je suppose que c'est la même chose; l' a.thisintérieur de run()doit se référer à la englobante a « s this. Ai-je raison? (C'est ainsi que le code minifié est dans les .jarfichiers de l'application OSX Kindle Previewer , j'essaie juste de comprendre ce que je regarde.)
Matt Mc
En Java, une classe interne peut ne pas avoir le même nom que l'une de ses classes englobantes (JLS 8.1), donc a.thisdans votre exemple n'est pas définie. Je ne sais pas si cette contrainte est vraie pour le bytecode. Peut être pas.
aioobe
56

Cela signifie l' thisinstance de la LocalScreenclasse externe .

L'écriture thissans qualificatif retournera l'instance de la classe interne dans laquelle l'appel se trouve.

SLaks
la source
4
Je ne comprends toujours pas. Quelle est la différence lorsque je le code comme "LocalScreen.this" par rapport à "this"? J'ai testé les deux et le compilateur n'a accepté que "LocalScreen.this". Le premier paramètre de aFuncCall attend une classe Parent qui est une classe parente de "Somethig".
Johnny Jazz
1
Je suis curieux de savoir cela aussi. Pouvez-vous nous expliquer ce que cela signifie? Je ne vois aucune classe interne définie dans le code ci-dessus; chaque fonction Java a-t-elle une classe anonyme associée distincte de la classe dont elle est membre?
poundifdef
4
@rascher: Des classes internes sont utilisées; l'OP ne les a pas inclus dans l'extrait de code. Cette syntaxe n'est prise en charge que dans une classe interne non statique.
SLaks
Bien que vous ayez fourni un lien vers la documentation officielle Java.
Krzysztof Tomaszewski
14

Le compilateur prend le code et fait quelque chose comme ça avec:

public class LocalScreen 
{
    public void method() 
    {
        new LocalScreen$1(this).run;
    }

    public String toString() 
    {
        return "A LocalScreen object"; 
    }

    public void onMake(LocalScreen ls) { /* ... */ }

    public static void main(String[] args) 
    {
        new LocalScreen().method();
    }
}

class LocalScreen$1
     extends Runnable
{
    final LocalScreen $this;

    LocalScreen$1(LocalScreen $this)
    {
        this.$this = $this;
    }

    public void run() 
    {
        // Prints "An anonymous Runnable"
        System.out.println(this.toString());

        // Prints "A LocalScreen object"
        System.out.println($this.toString());

        // Won't compile! 'this' is a Runnable!
        //onMake(this);

        // Compiles! Refers to enclosing object
        $this.onMake($this);
    }

    public String toString() 
    {
        return "An anonymous Runnable!";
    }
}

Comme vous pouvez le voir, lorsque le compilateur prend une classe interne, il la convertit en classe externe (il s'agissait d'une décision de conception prise il y a LONGTEMPS afin que les machines virtuelles n'aient pas besoin d'être modifiées pour comprendre les classes internes).

Lorsqu'une classe interne non statique est créée, elle a besoin d'une référence au parent pour pouvoir appeler des méthodes / accéder aux variables de la classe externe.

Le this à l'intérieur de ce qui était la classe interne n'est pas le type approprié, vous devez accéder à la classe externe pour obtenir le bon type pour appeler la méthode onMake.

TofuBière
la source
ne devrait pas new LocalScreen$1().run;être new LocalScreen$1(this).run;?
Diskutant
C'est une réponse sous-estimée à la question. Des trucs intéressants.
Pinkerton
12

Class.thisautorise l'accès à l'instance de la classe externe. Voir l'exemple suivant.

public class A
{
  final String name;
  final B      b;
  A(String name) {
    this.name = name;
    this.b = new B(name + "-b");
  }

  class B
  {
    final String name;
    final C      c;
    B(String name) {
      this.name = name;
      this.c = new C(name + "-c");
    }

    class C
    {
      final String name;
      final D      d;
      C(String name) {
        this.name = name;
        this.d = new D(name + "-d");
      }

      class D
      {
        final String name;
        D(String name) {
          this.name = name;
        }

        void printMe()
        {
          System.out.println("D: " + D.this.name); // `this` of class D
          System.out.println("C: " + C.this.name); // `this` of class C
          System.out.println("B: " + B.this.name); // `this` of class B
          System.out.println("A: " + A.this.name); // `this` of class A
        }
      }
    }
  }
  static public void main(String ... args)
  {
    final A a = new A("a");
    a.b.c.d.printMe();
  }
}

Ensuite, vous obtiendrez.

D: a-b-c-d
C: a-b-c
B: a-b
A: a
NawaMan
la source
La seule réponse bien expliquée jusqu'ici ... C'est en effet "Class.this permet d'accéder à l'instance de la classe externe" et non des choses comme "Class.this permet d'accéder à la classe externe this". Une classe n'a pas de "ceci", seules les instances ont pour se référencer elles-mêmes ...
Żabojad
-2

Je sais quelle est votre confusion. Je rencontre le problème tout à l'heure, il devrait avoir une scène spéciale pour les distinguer.

class THIS {
  def andthen = {
    new THIS {
      println(THIS.this.## + ":inner-THIS.this.##")
      println(this.## + ":inner-this.##")
      new THIS {
        println(THIS.this.## + ":inner-inner-THIS.this.##")
        println(this.## + ":inner-this.##")
      }
    }
  }
  def getInfo = {
    println(THIS.this.## + ":THIS.this.##")
    println(this.## + ":this.##")
  }
}

Vous pouvez voir la différence entre THIS.thiset thisdans la nouvelle opération THIS par hashcode (. ##)

test dans la console scala:

scala> val x = new THIS
x: THIS = THIS@5ab9b447

scala> val y = x.andthen
1522119751:inner-THIS.this.##
404586280:inner-this.##
1522119751:inner-inner-THIS.this.##
2027227708:inner-this.##
y: THIS = THIS$$anon$1@181d7f28

scala> x.getInfo
1522119751:THIS.this.##
1522119751:this.##

THIS.thispointez toujours vers la classe THIS externe qui est référencée par val x, mais qui thisest au-delà de la nouvelle opération anonyme.

LoranceChen
la source