Zona J

Zona J header image 2

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

25 de Agosto de 2008 às 17:26 por Ruben Badaró · 4 Comentários ·

É 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.

Tags: geral

4 respostas até ao momento ↓

  • PJG // Ago 25, 2008 at 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ó // Ago 25, 2008 at 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 // Ago 25, 2008 at 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 // Ago 25, 2008 at 22:22

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

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

    Sorry!

Deixe um comentário

XHTML: Pode usar estas tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>