2014-07-19 13 views
7

Z tym kodem:Dziedziczenie: Czy istnieje sposób na odkrycie klasy, z której wywołano metodę?

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

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

new Test() wypisze "Test" dwukrotnie. Jako początkujący oczekiwałem, że wyjście będzie "SuperTest/Test". Rozumiem teraz, dlaczego nie jest to możliwe i dlaczego domyślny this odnosi się tylko do typu dziecka.

Jednak nie mogę znaleźć tego, co powinno być whoAmI(), aby wydrukować wydruk SuperTest/Test. Powiedział inaczej: w jaki sposób whoAmI() może uzyskać dostęp do nazwy typu, z którego jest wywoływana?

EDYCJA: Zmieniam tytuł pytania na nowy, który lepiej opisuje problem. (dawniej: Dziedziczenie: czy istnieje konstrukcja "odpowiednika", która odnosi się do typu super z typu pochodnego).

Odpowiedz

5

Jest to oczekiwane zachowanie, ponieważ w obu przypadkach getType() jest wywoływany na tym samym obiekcie typu Test. Dzieje się tak, ponieważ JVM wie, że typ tworzonego obiektu jest pochodną klasy Test w momencie wywołania konstruktora podstawowego, więc jest to drukowany typ.

To zachowanie nie jest powszechne dla wszystkich języków programowania, ale działa w ten sposób w Javie.

Jeśli klasa chce uzyskać dostęp do własnego Class obiektu, może to zrobić statycznie, czyli używając wyrażenia SuperTest.class.

Aby uzyskać zachowanie które chcesz można przechodzić z klasy do whoAmI od konstruktora, na przykład:

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

Dzięki. Czy to znaczy, że nie ma dynamicznego sposobu na poznanie typu, w którym zdefiniowana jest dana metoda? Musimy zawsze zapewnić wartość statyczną? – mins

+0

@mins Możesz znaleźć klasę, w której metoda jest zdefiniowana za pomocą interfejsów API do refleksji, ale zwykle dostarczanie statycznego typu jest łatwiejsze. – dasblinkenlight

+1

Ups, miałem na myśli "klasę, z której metoda jest nazywana". Obawiam się, że odpowiedź nadal będzie ważna. – mins

0

Dla rozrywki, tutaj jest program testowy wykazując „WhoAmI”, który będzie drukować na Każda wymagana głębokość pochodzenia:

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

Drukuje żądaną liczbę przodków obiektu, a dla n = 1 jest taki sam, jak kod w pytaniu, prawda? W każdym razie interesujące! – mins

+0

@mins Prawidłowe. Zasadniczo jest to połączenie oryginalnego kodu i idei getSuperclass() w celu znalezienia przodka. –

0

jaki sposób whoAmI() dostępu nazwa typu jest „o nazwie z”?

Możesz uzyskać dostęp do superklasę użyciu Class.getSuperclass():

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

to technicznie odpowiedzi na swoje pytania, ale tylko dlatego, że zakłada się, że metoda omawiamy należy do nadrzędnej (który to robi w swoim przykładzie).

Ale może naprawdę chcesz znać odpowiedź na "W jaki sposób metoda może zidentyfikować, która klasa jest zdefiniowana, nawet jeśli jest wywoływana na instancji podklasy?" To byłaby znacznie bardziej skomplikowana sprawa. Podczas gdy java.lang.reflect.Method.getDeclaringClass() wydaje się robić to, o co prosisz, musisz najpierw zadeklarować instancję klasy "Class (tj. SuperTest.class), która pokonuje punkt - gdybyś to miał, nie musiałbyś w ogóle używać metody java.lang.reflect.Method.

Jednakże, jeśli mogę zmienić przesłankę trochę więcej, załóżmy osadzić każdy klasa ze statycznym instancji klasy jak ten:

private final static Class THIS_CLASS = SuperTest.class; 

Wtedy staje się możliwe, aby napisać metodę który nie ma dostępu klasę uznającą po imieniu, bo może założyć, że cokolwiek robi klasa kopiowaniem wklejone będą miały THIS_CLASS zdefiniowane:

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

Da to I am packagename.MinsSuperTest.whoAmI() jako wynik.

+0

Dzięki. Pierwsza wersja whoAmI wyświetli '" SuperTest/SuperTest "', co nie jest oczekiwanym wynikiem. Druga wersja rzeczywiście odpowiada na inny problem (wiedząc, gdzie metoda jest zdefiniowana, ale nie od tego, gdzie jest wywoływana). – mins