Java: Class.this

113

Mam program w Javie, który wygląda tak.

public class LocalScreen {

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

Co to LocalScreen.thisznaczy w aFuncCall?

Johnny Jazz
źródło

Odpowiedzi:

170

LocalScreen.thisodnosi się do thisotaczającej klasy.

Ten przykład powinien to wyjaśnić:

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

Wynik:

An anonymous Runnable!
A LocalScreen object

Ten post został przepisany jako artykuł tutaj .

aioobe
źródło
A co jeśli masz coś takiego: public class a { private class a { public void run() { System.out.println(a.this.toString()); } } Przypuszczam, że to ta sama sprawa; a.thisciągu run()musi odnosić się do załączania a „s this. Czy mam rację? (W ten sposób zminimalizowany kod znajduje się w .jarplikach aplikacji OSX Kindle Previewer , po prostu próbuję zrozumieć, na co patrzę.)
Matt Mc
W Javie klasa wewnętrzna może nie mieć takiej samej nazwy jak żadna z otaczających ją klas (JLS 8.1), więc a.thisw naszym przykładzie nie jest zdefiniowana. Nie wiem, czy to ograniczenie jest prawdziwe dla kodu bajtowego. Może nie.
aioobe
56

Oznacza thisinstancję LocalScreenklasy zewnętrznej .

Pisanie thisbez kwalifikatora zwróci wystąpienie klasy wewnętrznej , w której znajduje się wywołanie.

SLaks
źródło
4
Nadal nie do końca rozumiem. Jaka jest różnica, gdy zakoduję go jako „LocalScreen.this” w porównaniu z „this”? Przetestowałem oba i kompilator zaakceptował tylko „LocalScreen.this”. Pierwszy parametr aFuncCall oczekuje klasy aParent, która jest klasą nadrzędną „Somethig”.
Johnny Jazz
1
Też mnie to ciekawi. Czy możesz wyjaśnić, co to oznacza? Nie widzę żadnych klas wewnętrznych zdefiniowanych w powyższym kodzie; czy z każdą funkcją Java jest skojarzona klasa anonimowa, inna niż klasa, której jest członkiem?
poundifdef
4
@rascher: W użyciu są klasy wewnętrzne; OP nie uwzględnił ich we fragmencie kodu. Ta składnia jest obsługiwana tylko w niestatycznej klasie wewnętrznej.
SLaks
Fajnie, że podałeś link do oficjalnej dokumentacji Java.
Krzysztof Tomaszewski
14

Kompilator pobiera kod i robi z nim coś takiego:

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

Jak widać, gdy kompilator przyjmuje klasę wewnętrzną, konwertuje ją na klasę zewnętrzną (była to decyzja projektowa podjęta DŁUGO dawno temu, aby maszyny wirtualne nie musiały być zmieniane, aby zrozumieć klasy wewnętrzne).

Kiedy tworzona jest niestatyczna klasa wewnętrzna, potrzebuje odwołania do rodzica, aby mogła wywoływać metody / zmienne dostępu klasy zewnętrznej.

To, co było w klasie wewnętrznej, nie jest właściwym typem, musisz uzyskać dostęp do klasy zewnętrznej, aby uzyskać odpowiedni typ do wywołania metody onMake.

TofuBeer
źródło
nie powinno new LocalScreen$1().run;być new LocalScreen$1(this).run;?
Diskutant
To niedoceniana odpowiedź na to pytanie. Interesujące rzeczy.
Pinkerton
12

Class.thisumożliwia dostęp do instancji klasy zewnętrznej. Zobacz poniższy przykład.

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

Wtedy dostaniesz.

D: a-b-c-d
C: a-b-c
B: a-b
A: a
NawaMan
źródło
Jedyną dobrze wyjaśnioną odpowiedzią do tej pory… To rzeczywiście jest „Class.this umożliwia dostęp do instancji zewnętrznej klasy”, a nie rzeczy takie jak „Class.this umożliwia dostęp do zewnętrznej klasy this”. Klasa nie ma żadnego „tego”, tylko instancje mają się odwoływać ...
Żabojad
-2

Wiem, jakie jest twoje zdezorientowanie, właśnie teraz napotykam problem, powinna mieć specjalną scenę, aby je rozróżnić.

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

Możesz zobaczyć różnicę między THIS.thisi thisw nowej TEJ operacji przez hashcode (. ##)

test w konsoli 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.thiszawsze wskazują zewnętrzną TĘ klasę, do której odwołuje się wartość x, ale thisjest poza anonimową nową operacją.

LoranceChen
źródło