Abrufen des Namens einer Unterklasse aus einer Superklasse

77

Angenommen, ich habe eine Basisklasse mit dem Namen Entity. In dieser Klasse habe ich eine statische Methode, um den Klassennamen abzurufen:

class Entity {
    public static String getClass() {
        return Entity.class.getClass();
    }
}

Jetzt habe ich eine andere Klasse, die das erweitert.

class User extends Entity {
}

Ich möchte den Klassennamen des Benutzers erhalten:

System.out.println(User.getClass());

Mein Ziel ist es, die Ausgabe von "com.packagename.User" in der Konsole zu sehen, aber stattdessen werde ich "com.packagename.Entity" erhalten, da auf die Entity-Klasse direkt von der statischen Methode verwiesen wird.

Wenn dies keine statische Methode wäre, könnte dies leicht mithilfe des thisSchlüsselworts innerhalb der EntityKlasse (dh :) gelöst werden return this.class.getClass(). Ich brauche diese Methode jedoch, um statisch zu bleiben. Irgendwelche Vorschläge, wie man das angeht?

Matt Huggins
quelle

Antworten:

41

Nicht möglich. Statische Methoden sind in keiner Weise zur Laufzeit polymorph. Es ist absolut unmöglich, diese Fälle zu unterscheiden:

System.out.println(Entity.getClass());
System.out.println(User.getClass());

Sie kompilieren mit demselben Bytecode (vorausgesetzt, die Methode ist in definiert Entity).

Wie würden Sie diese Methode außerdem so nennen, dass es sinnvoll wäre, polymorph zu sein?

Michael Borgwardt
quelle
1
Vielen Dank für die Klarstellung, ich glaube, ich weiß nicht genug darüber, wie statische Methoden kompiliert werden. Dies hilft, die Dinge zu klären. Ich muss an einer alternativen Lösung arbeiten.
Matt Huggins
2
+1, genau richtig. Diese ganze Frage entsteht aus einem Missverständnis darüber, wie statische Methoden funktionieren.
Mark Peters
94

Machen Sie die Methode nicht statisch. Das Problem ist, dass Sie beim Aufrufen getClass()die Methode in der Superklasse aufrufen - statische Methoden werden nicht vererbt. Darüber hinaus sind Sie im Grunde Namensschatten Object.getClass(), was verwirrend ist.

Wenn Sie den Klassennamen innerhalb der Oberklasse protokollieren müssen, verwenden Sie

return this.getClass().getName();

Dies gibt "Entität" zurück, wenn Sie eine EntityInstanz haben, "Benutzer", wenn Sie eine UserInstanz haben usw.

matt b
quelle
1
thisexistiert nicht in einer statischen Methode.
Matt Huggins
32
@ Matt Huggins, deshalb sagt der erste Satz in meiner AntwortDon't make the method static
matt b
2
@ matt b - deshalb sagt der vorletzte Satz meiner FrageHowever, I need this method to remain static.
Matt Huggins
@ MattHuggins Ich frage mich, warum eine Pojo-Methode nicht funktionieren würde. Wenn Sie eine statische Methode aufrufen, müssen Sie auf den Klassennamen verweisen, der bereits im Code enthalten ist. Wenn Sie die Unterklasse instanziiert haben, die Referenz jedoch zur Oberklasse gehört, haben Sie ein Objekt und müssen die statische Methode nicht aufrufen.
Mindwin
5
Auch wenn diese Antwort nicht "Ich brauche diese Methode, um statisch zu bleiben" anspricht, ist es nützlich, wenn Sie (wie ich) hier gelandet sind und nach einer Möglichkeit suchen, die Klasse in eine Methode zu bringen, die nicht statisch sein musste. ;)
Ryan Heathcote
6

Ihre Frage ist nicht eindeutig, aber soweit ich Ihnen sagen kann, möchten Sie die aktuelle Klasse anhand einer statischen Methode kennen. Die Tatsache, dass Klassen voneinander erben, ist irrelevant, aber für die Diskussion habe ich es auch so implementiert.

Klasse Eltern {
    public static void printClass () {
      System.out.println (Thread.currentThread (). GetStackTrace () [2] .getClassName ());
    }}
}}

public class Test erweitert Parent {
    public static void main (String [] args) {
      printClass ();
    }}
}}
Gilead
quelle
Autsch, dieser Stacktrace-Hack ist dreckig. Was ist los mit System.out.println(Parent.class.getName());?
whiskeysierra
2
Wie gesagt, Matts Frage ist nicht eindeutig und ich habe eine der möglichen Antworten gegeben. Offensichtlich liefert Ihre Version nicht die gleiche Ausgabe wie mein Code, daher gibt es keinen Vergleich - Sie sprechen von einer anderen Lösung. Und das Lesen des Thread-Stacks ist kein Hack, sondern eine normale Methode zum Überprüfen der Anrufhierarchie (natürlich habe ich die Überprüfung der Integrität übersprungen, da dies ein Beispiel ist).
Gilead
Dies ist ein ziemlich lustiger Hack, hat aber wenig mit dem angeforderten Verhalten zu tun. Diese Funktion gibt den Namen der Klasse aus, die die Methode aufruft (dh wenn Sie extends Parentden Aufruf entfernen und in ändern Parent.printClass(), wird weiterhin "Test" ausgegeben).
Nemetroid
6

Das funktioniert bei mir

this.getClass().asSubclass(this.getClass())

Aber ich bin mir nicht sicher, wie es funktioniert.

sirolf2009
quelle
3

Die Oberklasse sollte nicht einmal von der Existenz der Unterklasse wissen, geschweige denn Operationen ausführen, die auf dem vollständig qualifizierten Namen der Unterklasse basieren. Wenn Sie Operationen benötigen, die auf der genauen Klasse basieren, und die erforderliche Funktion nicht durch Vererbung ausführen können, sollten Sie Folgendes tun:

public class MyClassUtil
{
    public static String doWorkBasedOnClass(Class<?> clazz)
    {
        if(clazz == MyNormalClass.class)
        {
            // Stuff with MyNormalClass
            // Will not work for subclasses of MyNormalClass
        }

        if(isSubclassOf(clazz, MyNormalSuperclass.class))
        {
            // Stuff with MyNormalSuperclass or any subclasses
        }

        // Similar code for interface implementations
    }

    private static boolean isSubclassOf(Class<?> subclass, Class<?> superclass)
    {
        if(subclass == superclass || superclass == Object.class) return true;

        while(subclass != superclass && subclass != Object.class)
        {
            subclass = subclass.getSuperclass();
        }
        return false;
    }
}

(Ungetesteter Code)

Diese Klasse kennt auch ihre eigenen Unterklassen nicht, sondern verwendet die ClassKlasse zum Ausführen von Operationen. Höchstwahrscheinlich ist es immer noch eng mit Implementierungen verbunden (im Allgemeinen eine schlechte Sache, oder wenn nicht schlecht, ist es nicht besonders gut ), aber ich denke, eine Struktur wie diese ist besser als eine Oberklasse, die herausfindet, was alle ihre Unterklassen sind.

Brian S.
quelle
2

Warum möchten Sie Ihre eigene getClass () -Methode implementieren? Sie können einfach verwenden

System.out.println(User.class);

Bearbeiten (um etwas näher darauf einzugehen): Sie möchten, dass die Methode statisch ist. In diesem Fall müssen Sie die Methode für die Klasse aufrufen, deren Klassenname Sie möchten, sei es die Unterklasse oder die Superklasse. Anstatt MyClass.getClass()anzurufen, können Sie einfach MyClass.classoder anrufen MyClass.class.getName().

Außerdem erstellen Sie eine staticMethode mit derselben Signatur wie die Object.getClass()Instanzmethode, die nicht kompiliert werden kann.

Samitgaur
quelle
1
Ich habe die Frage absichtlich geschrieben, damit sie bei StackOverflow sehr einfach ist und direkt zum Punkt der Frage kommt. Das liegt wirklich daran, dass ich eine viel robustere statische Methode erstelle, die den Klassennamen einer beliebigen Unterklasse verwendet, die diese abstrakte Basisklasse erweitert.
Matt Huggins
1
Klingt so, als ob Sie Polymorphismus durch staticMethoden wollen, was nicht möglich ist. Können Sie auch erläutern, warum das oben Genannte nicht robust genug ist?
Samitgaur
Es ist nicht robust genug, da ich die statische Methode in jeder einzelnen erbenden Klasse neu definieren müsste, anstatt die statische Methode nur einmal definieren zu können.
Matt Huggins
1
Wenn Sie statisch verwenden möchten, kennen Sie die konkrete Klasse bereits. Was ist los mit User.class.getName()?
whiskeysierra
Denn wenn ich User.class.getName()in Entitydie statische Methode fest codiere , was passiert dann, wenn ich Entitymit einer neuen Klasse namens Profileaufrufe? Es wird weiterhin der Klassenname für Benutzer anstelle des Klassennamens für Profil zurückgegeben.
Matt Huggins
2
  1. Erstellen Sie eine Member-String-Variable in der Oberklasse.
  2. Fügen Sie this.getClass (). GetName () zu einem Konstruktor hinzu, der den Wert in der Member-String-Variablen speichert.
  3. Erstellen Sie einen Getter, um den Namen zurückzugeben.

Jedes Mal, wenn die erweiterte Klasse instanziiert wird, wird ihr Name in der Zeichenfolge gespeichert und ist mit dem Getter zugänglich.

Dawg 'N IT
quelle
0

Eine statische Methode ist einer Klasse zugeordnet, nicht einem bestimmten Objekt.

Überlegen Sie, wie dies funktionieren würde, wenn mehrere Unterklassen vorhanden wären - z. B. ist Administrator auch eine Entität. Wie würde Ihre statische Entitätsmethode, die nur der Entitätsklasse zugeordnet ist, wissen, welche Unterklasse Sie möchten?

Du könntest:

  • Verwenden Sie die vorhandene Methode getClass ().
  • Übergeben Sie ein Argument an Ihre statische Methode getClass () und rufen Sie eine Instanzmethode für dieses Objekt auf.
  • Machen Sie Ihre Methode nicht statisch und benennen Sie sie um.
Andy Thomas
quelle
0

Wenn ich Ihre Frage richtig verstehe, können Sie meiner Meinung nach nur das erreichen, was Sie wollen, indem Sie die statische Methode in jeder Unterklasse erneut implementieren, zum Beispiel:

class Entity {
    public static String getMyClass() {
        return Entity.class.getName();
    }
}

class Derived extends Entity {
    public static String getMyClass() {
        return Derived.class.getName();
    }
}

Dadurch werden package.Entity und package.Derived nach Bedarf gedruckt. Chaotisch, aber hey, wenn das deine Einschränkungen sind ...

Brabster
quelle
0

Wenn ich es richtig mache, möchten Sie Ihre Unterklasse in der Basisklasse in der statischen Methode verwenden. Ich denke, Sie können dies tun, indem Sie einen Klassenparameter an die Methode übergeben

class Entity {
    public static void useClass(Class c) {
        System.out.println(c);
        // to do code here
    }
}

class User extends Entity {
}

class main{
    public static void main(String[] args){
        Entity.useClass(Entity.class);
    }
}
Dis Ehrlich
quelle
0

Mein Kontext: Entität der Oberklasse mit Unterklassen für XML-Objekte. Meine Lösung: Erstellen Sie eine Klassenvariable in der Oberklasse

Class<?> claz;

Dann würde ich in der Unterklasse die Variable der Oberklasse im Konstruktor setzen

public class SubClass {
   public SubClass() {
     claz = this.getClass();
   }
}
Matthias dirickx
quelle
-4

es ist sehr einfach gemacht von

User.getClass (). GetSuperclass ()

NightGale
quelle
2
Dies ist das Gegenteil von dem, was die Frage verlangt. Ich wollte den Namen einer Unterklasse innerhalb einer Oberklasse. Ihre Antwort enthält den Namen der Oberklasse innerhalb einer Unterklasse.
Matt Huggins