Wie überprüfe ich das (generische (generisches Argument))?

Hintergrund (über den wir uns eigentlich keine Sorgen machen müssen)

Dies ist eine Frage abgeleitet vonErstellen Sie einen generischen Baum mit Vererbung . Ich öffne diese Frage als separate Frage, da dies nicht nur mit einem Tree-Problem zusammenhängt. Dies ist eher ein generisches und ein Klassenproblem.

Frage

Zur besseren Veranschaulichung durch Codes haben wir aTree Klasse aSubTree Klasse und aWrongSubTree Klasse:

class Tree<TREE extends Tree<?,?>, DATA> {
}
class SubTree<STREE extends SubTree<?,?>, DATA> extends Tree<STREE, DATA> {
}
class WrongSubTree<WSTREE extends Tree<?,?>, DATA> extends Tree<WSTREE, DATA> {
}

Bei der Objekterstellung möchten wir überprüfen, ob das generische Argument der Klasse des Objekts selbst entspricht:

Tree<Tree<?,?>, String> tree01 = new Tree<Tree<?,?>, String>();                 // equals : OK
Tree<SubTree<?,?>, String> tree02 = new Tree<SubTree<?,?>, String>();           // (!) not equals
SubTree<SubTree<?,?>, String> tree03 = new SubTree<SubTree<?,?>, String>();     // equals : OK
WrongSubTree<Tree<?,?>, String> tree04 = new WrongSubTree<Tree<?,?>, String>(); // (!) not equals

(Beachten Sie, dass die 4 obigen Zeilen vorerst keine Kompilierungsfehler und keine Laufzeitausnahmen enthalten.)

Mein Versuch

Dazu versuchen wir aClass<> Parameter in den Konstruktoren:

class Tree<TREE extends Tree<?,?>, DATA> {
    public Tree(Class<TREE> clazz) {
        System.out.println(this.getClass());
        System.out.println(clazz);
        System.out.println();
        if (this.getClass() != clazz)
            throw new RuntimeException();
    }
}
class SubTree<STREE extends SubTree<?,?>, DATA> extends Tree<STREE, DATA> {
    public SubTree(Class<STREE> clazz) {
        super(clazz);
    }
}
class WrongSubTree<WSTREE extends Tree<?,?>, DATA> extends Tree<WSTREE, DATA> {
    public WrongSubTree(Class<WSTREE> clazz) {
        super(clazz);
    }
}

(Die obigen Klassendefinitionen sind gültige Java-Codes.)

Problem

Aber ich weiß nicht, wie ich diesen Konstruktor nennen soll:

Tree<Tree<?,?>, String> tree01a = new Tree<Tree<?,?>, String>(Tree.class);
// The constructor Tree<Tree<?,?>,String>(Class<Tree>) is undefined

Tree<Tree<?,?>, String> tree01b = new Tree<Tree<?,?>, String>(Tree<?,?>.class);
// Syntax error, insert "Dimensions" to complete ArrayType

Die beiden obigen Zeilen verursachen Kompilierungsfehler.

Ich denke, es liegt am Konstruktorpublic Tree(Class<TREE> clazz) {} erwartetClass<Tree<?,?>>, aber nichtClass<Tree>. Das können wir jedoch nichtTree<?,?>.class.

Der Grund ist, ich habe versucht, die Klasse zu ändern:

class Tree<TREE extends Tree<?,?>, DATA> {
    public Tree(Class<Tree> clazz) {            // changed
        System.out.println(this.getClass());
        System.out.println(clazz);
        System.out.println();
        if (this.getClass() != clazz)
            throw new RuntimeException();
    }
}
Tree<Tree<?,?>, String> tree01a = new Tree<Tree<?,?>, String>(Tree.class);

Es liegt kein Kompilierungsfehler vor.

Das Folgende verursacht jedoch denselben Kompilierungsfehler:

class Tree<TREE extends Tree<?,?>, DATA> {
    public Tree(Class<Tree<?,?>> clazz) {       // changed
        System.out.println(this.getClass());
        System.out.println(clazz);
        System.out.println();
        if (this.getClass() != clazz)
            throw new RuntimeException();
    }
}
Tree<Tree<?,?>, String> tree01a = new Tree<Tree<?,?>, String>(Tree.class);
// The constructor Tree<Tree<?,?>,String>(Class<Tree>) is undefined
Bearbeiten Sie # 1

Beyogen aufmein Kommentar Unten habe ich es versucht. Hoffe, es hilft für einige Inspirationen.

static class SimpleClass<T> {
    private SimpleClass(Object dummy) {
        // dummy constructor, avoid recursive call of the default constructor
    }
    SimpleClass() {
        SimpleClass<T> myself = new SimpleClass<T>(new Object()) {};
        System.out.println(((ParameterizedType) myself.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        // prints "T"

        TypeReference<SimpleClass<T>> typeRef = new TypeReference<SimpleClass<T>>() {};
        System.out.println(typeRef.getType());
        // prints "Main.Main$SimpleClass<T>"
    }
    void func() {
        SimpleClass<T> myself = new SimpleClass<T>(new Object()) {};
        System.out.println(((ParameterizedType) myself.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        // prints "T"

        TypeReference<SimpleClass<T>> typeRef = new TypeReference<SimpleClass<T>>() {};
        System.out.println(typeRef.getType());
        // prints "Main.Main$SimpleClass<T>"
    }
}

public static void main(String[] args) {
    SimpleClass<String> simpleObj = new SimpleClass<String>();
    simpleObj.func();

    SimpleClass<String> outsideSimpleClass = new SimpleClass<String>(){};
    System.out.println(((ParameterizedType) outsideSimpleClass.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    // prints "class java.lang.String"
}

Beachten wir nochkann nicht get "class java.lang.String"Innerhalb SimpleClass.

Noch wichtiger ist, wenn wir das Type-Argument verwenden<T> ein Objekt aus instanziiereneine andere Klasse, wir können immer noch nicht den Typparameter davon bekommen:

static class AnotherClass<T> {
    private AnotherClass(Object dummy) {}
}
static class SimpleClass<T> {
    SimpleClass() {
        AnotherClass<T> another = new AnotherClass<T>(new Object()) {};
        System.out.println(((ParameterizedType) another.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        // prints "T"

        TypeReference<AnotherClass<T>> anotherTypeRef = new TypeReference<AnotherClass<T>>() {};
        System.out.println(anotherTypeRef.getType());
        // prints "Main.Main$AnotherClass<T>"
    }
    void func() {
        AnotherClass<T> another = new AnotherClass<T>(new Object()) {};
        System.out.println(((ParameterizedType) another.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        // prints "T"

        TypeReference<AnotherClass<T>> anotherTypeRef = new TypeReference<AnotherClass<T>>() {};
        System.out.println(anotherTypeRef.getType());
        // prints "Main.Main$AnotherClass<T>"
    }
}

Beachten Sie, dass dies das Type-Argument von bedeutetAnotherClass kann nicht in enthüllt werdenSimpleClass, wo es ein Ort außerhalb der Klasse selbst ist!

Nach meinem Verständnis können wir nur die anonyme Unterklasse & verwenden.getGenericSuperclass() trick an einem ort wo es eigentlich schon die antwort kennt. Wie inmain()Hier ist der Ort, an demclass java.lang.String ist wirklich als das Typargument definiert.

(IMO, wenn die Fähigkeit dieses Tricks so eingeschränkt ist, ist es überhaupt nicht wirklich nützlich.)

Antworten auf die Frage(1)

Ihre Antwort auf die Frage