2014-07-19 20 views
7

Con questo codice:Ereditarietà: esiste un modo per scoprire la classe da cui è stato chiamato un metodo?

class SuperTest { 
    SuperTest() { whoAmI(); } 
    void whoAmI() { System.out.println(getClass().getName()); } 
} 

class Test extends SuperTest { 
    Test() { whoAmI(); }   
} 

new Test() stamperà "Test" due volte. Come principiante mi aspettavo che l'uscita fosse "SuperTest/Test". Capisco ora perché questo non è possibile, e perché l'implicito this farà riferimento solo al tipo di bambino.

Tuttavia non riesco a trovare che cosa dovrebbe essere whoAmI() per stampare effettivamente l'output SuperTest/Test. Detto altrimenti: come può whoAmI() accedere al nome del tipo "viene chiamato da"?

MODIFICA: Sto cambiando il titolo della domanda per uno nuovo che descrive meglio il problema. (old was: Ereditarietà: esiste un costrutto "this-equivalent" per fare riferimento al tipo super dal tipo derivato).

risposta

5

Questo è il comportamento previsto, poiché in entrambi i casi viene chiamato lo getType() sullo stesso oggetto di tipo Test. Questo perché JVM sa che il tipo dell'oggetto creato è della classe derivata Test nel momento in cui viene chiamato il costruttore base, quindi questo è il tipo da stampare.

Questo comportamento non è universale per tutti i linguaggi di programmazione, ma funziona in questo modo in Java.

Se una classe vuole accedere a una propria Class oggetto, può farlo staticamente, vale a dire utilizzando l'espressione SuperTest.class.

per ottenere il comportamento che si vuole si può passare alla classe di whoAmI dal costruttore, in questo modo:

class SuperTest { 
    SuperTest() { whoAmI(SuperTest.class); } 
    void whoAmI(Class c) { System.out.println(c.getName()); } 
} 
class Test extends SuperTest { 
    Test() { whoAmI(Test.class); }   
} 
+0

Grazie. Significa che non esiste un modo dinamico per conoscere il tipo in cui è definito un metodo? Dobbiamo sempre fornire un valore statico? – mins

+0

@mins È possibile trovare la classe in cui il metodo viene definito utilizzando le API di reflection, ma in genere fornire un tipo statico è più semplice. – dasblinkenlight

+1

Oops, intendevo "la classe da cui viene chiamato il metodo". Ma temo che la risposta sarà ancora valida. – mins

0

Per il vostro divertimento, ecco un programma di test che dimostra una "whoami" che stamperà ogni profondità di ascendenza richiesta:

public class Test { 
    public static void main(String[] args) { 
    Sub2 sub2 = new Sub2(); 
    System.out.println("Four levels"); 
    sub2.whoAmi(4); 
    System.out.println("Two levels"); 
    sub2.whoAmi(2); 
    } 
    public void whoAmi(int levels){ 
    if(levels > 1){ 
     Class<? extends Object> ancestor = getClass(); 
     for(int i=1; i<levels && ancestor != null; i++){ 
     ancestor = ancestor.getSuperclass(); 
     } 
     if(ancestor != null){ 
     System.out.println(ancestor.getName()); 
     } 
     whoAmi(levels - 1); 
    } else { 
     System.out.println(getClass().getName());  
    } 
    } 
} 

class Sub1 extends Test { 
} 

class Sub2 extends Sub1 { 
} 
+0

Stampa il numero desiderato di antenati dell'oggetto e per n = 1 è uguale al codice nella domanda, giusto? Comunque interessante! – mins

+0

@mins Corretto. È essenzialmente la combinazione del codice originale e dell'idea getSuperclass() per trovare un antenato. –

0

come può whoAmI() accesso il nome del tipo è "chiamato da"?

È possibile accedere alla superclasse utilizzando Class.getSuperclass():

void whoAmI() { 
    Class superclass = getClass().getSuperclass(); 
    String superclassText = "no superclass found"; 
    if (superclass != null) superclassText = superclass.getName(); 
    System.out.println(superclassText); 
} 

Questo risponde tecnicamente alla tua domanda, ma solo perché ho assunto che il metodo stiamo discutendo appartiene alla superclasse (cosa che fa nel tuo esempio).

Ma forse si vuole veramente sapere la risposta a "Come può un metodo identificare quale classe è definita anche se è invocata su un'istanza di una sottoclasse?" Questa sarebbe una questione molto più complicata. Mentre java.lang.reflect.Method.getDeclaringClass() sembra fare quello che chiedi, per prima cosa hai bisogno dell'istanza di classe Class della classe dichiarante (ad esempio, SuperTest.class) che sconfigge il punto: se lo avessi fatto, non avresti bisogno di usare java.lang.reflect.Method.

Tuttavia, se posso cambiare la premessa un po 'di più, supponiamo di incorporare ogni classe con un'istanza di classe statico come questo:

private final static Class THIS_CLASS = SuperTest.class; 

Allora diventa possibile scrivere un metodo di che non accede sua classe dichiarando per nome, perché si può supporre che a seconda di quale classe si ottiene copia-incollato avrà THIS_CLASS definito:

void whoAmI() { 
    final String myName = "whoAmI"; 
    final Class[] myParamTypes = {}; 
    String result; 
    try { 
     Method thisMethod = THIS_CLASS.getDeclaredMethod(myName, myParamTypes); 
     result = "I am " + thisMethod.getDeclaringClass().getName() + "." + 
       myName + Arrays.toString(myParamTypes); 
     // Now swap the square brackets from Arrays.toString for parentheses 
     result = result.replaceAll("\\[", "\\(").replaceAll("\\]", "\\)"); 
    } catch (NoSuchMethodException | SecurityException ex) { 
     result = ex.toString(); 
    } 
    System.out.println(result); 
} 

Ciò produrrà I am packagename.MinsSuperTest.whoAmI() come output.

+0

Grazie. La prima versione di whoAmI stamperà '" SuperTest/SuperTest "', che non è l'output previsto. La seconda versione risponde effettivamente ad un altro problema (sapere dove è definito il metodo, ma non da dove viene chiamato). – mins

Problemi correlati