Java: Class.this


112

Eu tenho um programa Java parecido com este.

public class LocalScreen {

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

O que LocalScreen.thissignifica em aFuncCall?

Respostas:


169

LocalScreen.thisrefere-se a thisda classe envolvente.

Este exemplo deve explicar isso:

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

Resultado:

An anonymous Runnable!
A LocalScreen object

Esta postagem foi reescrita como um artigo aqui .


E se você tiver algo como: public class a { private class a { public void run() { System.out.println(a.this.toString()); } } Suponho que seja a mesma coisa; o a.thisprazo run()deve se referir ao delimitador a é this. Estou certo? (É assim que o código minificado está nos .jararquivos do aplicativo OSX Kindle Previewer , só estou tentando entender o que estou vendo.)
Matt Mc

Em Java, uma classe interna pode não ter o mesmo nome de nenhuma de suas classes incluídas (JLS 8.1), portanto, a.thisem seu exemplo não está definido. Não sei se essa restrição é verdadeira para bytecode. Talvez não.
aioobe

56

Significa a thisinstância da LocalScreenclasse externa .

Escrever thissem um qualificador retornará a instância da classe interna da qual a chamada está inserida.


4
Ainda não entendo muito bem. Qual é a diferença quando eu codifico como "LocalScreen.this" em comparação com "this"? Testei ambos e o compilador só aceitou "LocalScreen.this". O primeiro parâmetro de aFuncCall espera a classe aParent, que é uma classe pai de "Somethig".
Johnny Jazz,

1
Estou curioso sobre isso também. Você pode fornecer alguns detalhes sobre o que isso significa? Não vejo nenhuma classe interna definida no código acima; cada função Java tem uma classe anônima associada separada da classe da qual é membro?
poundifdef

4
@rascher: Existem classes internas em uso; o OP não os incluiu no snippet de código. Essa sintaxe só é compatível com uma classe interna não estática.
SLaks

Que bom que você forneceu um link para a documentação oficial do Java.
Krzysztof Tomaszewski

14

O compilador pega o código e faz algo assim com ele:

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

Como você pode ver, quando o compilador pega uma classe interna, ele a converte em uma classe externa (essa foi uma decisão de design feita MUITO tempo atrás para que as VMs não precisassem ser alteradas para entender as classes internas).

Quando uma classe interna não estática é criada, ela precisa de uma referência ao pai para que possa chamar métodos / variáveis ​​de acesso da classe externa.

O this dentro do que era a classe interna não é o tipo adequado, você precisa obter acesso à classe externa para obter o tipo certo para chamar o método onMake.


não deveria new LocalScreen$1().run;ser new LocalScreen$1(this).run;?
Diskutant

Esta é uma resposta subestimada à pergunta. Coisas interessantes.
Pinkerton

12

Class.thispermite o acesso à instância da classe externa. Veja o seguinte exemplo.

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

Então você vai conseguir.

D: a-b-c-d
C: a-b-c
B: a-b
A: a

A única resposta bem explicada até agora ... É de fato "Class.this permite acesso à instância da classe externa" e não coisas como "Class.this permite acesso à classe externa this". Uma classe não tem nenhum "isto", apenas as instâncias têm para referenciar a si mesmas ...
Żabojad

-2

Eu sei qual é a sua confusão. Estou encontrando o problema agora mesmo, deveria haver uma cena especial para distingui-los.

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

Você pode ver a diferença entre THIS.thise thisna nova operação THIS por hashcode (. ##)

teste no console do 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.thissempre aponte para ESTA classe externa que é referenciada por val x, mas thisestá além da nova operação anônima.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.