“Em que classe estou”, diz o Object para o outro

by Ruben Badaró on 25 Agosto, 2008

in geral

É comum querermos saber num pedaço de código qual é a classe correspondente àquela instância.
Isto é perfeitamente simples quando temos uma instancia:

this.getClass();

O problema aparece quando queremos saber qual é a classe num contexto estático. Pode até parecer estranho que se venha a necessitar de fazer isto, mas garanto que já precisei disto várias vezes.

Portanto, se não temos uma instância, como obtemos a Class? Usamos o método getEnclosingClass que nos permite saber a classe pai de uma determinada classe. Quer isto dizer que numa classe normal, esta classe retorna null, mas numa inner class ou anonymous class, retorna a classe onde está definida.
Como tal, criamos uma anonymous class da forma mais simples que se possa:

new Object(){}.getEnclosingClass();

Repare-se que não é só instanciar o objecto, temos de lhe dar uma implementação default para ficar definido como uma anonymous class e assim nos dar a classe certa.

No entanto, este método tem uma limitação importante. Suponhamos que temos as seguintes classes:

public class A {
  public String getMyName() {
    return this.getClass().getName();
  }
}
public class B extends A {
}

Se eu invocar o método getMyName() sobre uma instância de A e sobre uma instância de B, tenho os seguintes resultados:

new A().getMyName() -> "A"
new B().getMyName() -> "B"

Tudo normal e lógico, temos o nome da classe que foi instanciada.
Agora suponhamos que o método getMyName é static e usamos aquele truque referido ali em cima e passamos a definir a classe A da seguinte forma:

public class A {
  public static String getMyName() {
    return new Object(){}.getEnclosingClass().getName();
  }
}

Ao testarmos invocar o método em A e B, obtemos:

A.getMyName() -> "A"
B.getMyName() -> "A"

Ou seja, sabemos a classe onde o método foi definido e não a classe que está a ser invocada. Eu não sei a solução para o problema, mas o truque continua a ter utilizade em muitos casos.

{ 4 comments… read them below or add one }

PJG 25 Agosto, 2008 ás 21:23

public class GetMyClassTrial {

public static void main(String[] args) {
System.out.println(”I know my class : ” + GetMyClassTrial.class.getName());
System.out.println(”I don’t know my class : ” + Thread.currentThread().getStackTrace()[2].getClassName());
}

}

Ruben Badaró 25 Agosto, 2008 ás 21:56

Tinha-me enganado no segundo output, ambas as respostas davam A.

Penso que a tua solução também me dá a superclasse e nunca a subclasse. E o que eu queria era saber qual a classe que foi utilizada para invocar o método (no meu exemplo, que me desse B e não A)

PJG 25 Agosto, 2008 ás 22:20

Não tinha percebido bem.

Mas a verdade é que apenas te será possível encontrar qual a classe em que o método foi DECLARADO.

A menos que estejas num ambiente muito permissivo, com o código compilado com informação de debug (para ter etiquetas de número de linha no bytecode) , e utilizes a informação na stack trace para analisar o código que invocou o método, já que as duas chamadas que apresentas são representadas por bytecode diferente. Algo do tipo:

INVOKESTATIC org/zonaj/whatever/package/A$getMyName()V

INVOKESTATIC org/zonaj/whatever/package/B$getMyName()V

=:o)

Have Fun!

PJG 25 Agosto, 2008 ás 22:22

Onde escrevi:
“o código que invocou o método”

…queria escrever:
“o bytecode que invocou o método”

Sorry!

Leave a Comment