Java: Class.this

112

Ich habe ein Java-Programm, das so aussieht.

public class LocalScreen {

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

Was LocalScreen.thisbedeutet in aFuncCall?

Johnny Jazz
quelle

Antworten:

169

LocalScreen.thisbezieht sich auf thisdie einschließende Klasse.

Dieses Beispiel sollte es erklären:

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();
    }
}

Ausgabe:

An anonymous Runnable!
A LocalScreen object

Dieser Beitrag wurde bereits als ein Artikel neu geschrieben hier .

aioobe
quelle
Was ist, wenn Sie so etwas haben: public class a { private class a { public void run() { System.out.println(a.this.toString()); } } Ich nehme an, dass es die gleiche Sache ist; die a.thisin run()an der verweisen umschließenden a ‚s this. Habe ich recht? (So ​​ist der minimierte Code in den .jarDateien der OSX Kindle Previewer-App . Ich versuche nur zu verstehen, was ich sehe.)
Matt Mc
In Java hat eine innere Klasse möglicherweise nicht denselben Namen wie eine ihrer umschließenden Klassen (JLS 8.1), daher ist sie a.thisin Ihrem Beispiel nicht definiert. Ich weiß nicht, ob diese Einschränkung für Bytecode gilt. Vielleicht nicht.
Aioobe
56

Es bedeutet die thisInstanz der äußeren LocalScreenKlasse.

Wenn Sie thisohne Qualifizierer schreiben, wird die Instanz der inneren Klasse zurückgegeben , in der sich der Aufruf befindet.

SLaks
quelle
4
Ich verstehe es immer noch nicht ganz. Was ist der Unterschied, wenn ich es als "LocalScreen.this" im Vergleich zu "this" codiere? Ich habe beide getestet und der Compiler hat nur "LocalScreen.this" akzeptiert. Der erste Parameter von aFuncCall erwartet eine aParent-Klasse, die eine übergeordnete Klasse von "Somethig" ist.
Johnny Jazz
1
Ich bin auch neugierig darauf. Können Sie uns etwas näher erläutern, was dies bedeutet? Ich sehe keine inneren Klassen, die im obigen Code definiert sind. Hat jede Java-Funktion eine zugeordnete anonyme Klasse, die von der Klasse, zu der sie gehört, getrennt ist?
Poundifdef
4
@rascher: Es werden innere Klassen verwendet. Das OP hat sie nicht in das Code-Snippet aufgenommen. Diese Syntax wird nur in einer nicht statischen inneren Klasse unterstützt.
SLaks
Schön, dass Sie einen Link zur offiziellen Java-Dokumentation bereitgestellt haben.
Krzysztof Tomaszewski
14

Der Compiler nimmt den Code und macht so etwas damit:

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!";
    }
}

Wie Sie sehen können, konvertiert der Compiler eine innere Klasse in eine äußere Klasse (dies war eine Entwurfsentscheidung, die vor langer Zeit getroffen wurde, sodass VMs nicht geändert werden mussten, um innere Klassen zu verstehen).

Wenn eine nicht statische innere Klasse erstellt wird, benötigt sie einen Verweis auf das übergeordnete Element, damit sie Methoden / Zugriffsvariablen der äußeren Klasse aufrufen kann.

Da dies innerhalb der inneren Klasse nicht der richtige Typ ist, müssen Sie Zugriff auf die äußere Klasse erhalten, um den richtigen Typ für den Aufruf der onMake-Methode zu erhalten.

TofuBeer
quelle
sollte nicht new LocalScreen$1().run;sein new LocalScreen$1(this).run;?
Diskutant
Dies ist eine unterschätzte Antwort auf die Frage. Interessantes Zeug.
Pinkerton
12

Class.thisErmöglicht den Zugriff auf die Instanz der äußeren Klasse. Siehe folgendes Beispiel.

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();
  }
}

Dann wirst du bekommen.

D: a-b-c-d
C: a-b-c
B: a-b
A: a
NawaMan
quelle
Die einzige bisher gut erläuterte Antwort ... Es ist in der Tat "Klasse. Dies ermöglicht den Zugriff auf die Instanz der äußeren Klasse" und nicht Dinge wie "Klasse. Dies ermöglicht den Zugriff auf das Dies der äußeren Klasse". Eine Klasse hat kein "dies", nur Instanzen, um sich selbst zu referenzieren ...
Żabojad
-2

Ich weiß, was Ihre Verwirrung ist. Ich bin gerade auf das Problem gestoßen, es sollte eine spezielle Szene haben, um sie zu unterscheiden.

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.##")
  }
}

Sie können den Unterschied zwischen THIS.thisund thisin der neuen DIESEN Operation anhand des Hashcodes (. ##) erkennen.

Test in der Scala-Konsole:

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.thisZeigen Sie immer auf die äußere DIESE Klasse, auf die durch val x verwiesen wird, die jedoch thisüber eine anonyme neue Operation hinausgeht.

LoranceChen
quelle