2

背景(我们真的不需要担心)

这是一个源自Build A Generic Tree With Inheritance的问题。我将此作为一个单独的问题打开,因为这不仅与树问题有关。这更像是一个通用和类问题。

问题

为了更好地通过代码说明,我们有一个Tree类,一个SubTree类,一个WrongSubTree类:

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> {
}

在创建对象时,我们想检查泛型参数是否等于对象本身的类:

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

(请注意,以上 4 行目前没有编译错误和运行时异常。)

我的审判

为此,我们尝试Class<>在构造函数中添加一个参数:

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

(上面的类定义是有效的java代码。)

问题

但我不知道如何调用该构造函数:

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

以上两行都会导致编译错误。

我想这是因为构造函数public Tree(Class<TREE> clazz) {}期待Class<Tree<?,?>>,但不是Class<Tree>。但是,我们做不到Tree<?,?>.class

原因是我试图将课程更改为:

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

没有编译错误。

但是,以下导致相同的编译错误:

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

编辑#1

根据下面的评论,我尝试了这个。希望对一些灵感有所帮助。

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"
}

请注意,我们仍然无法在内部 获取“class java.lang.String” SimpleClass

更重要的是,如果我们使用 Type Argument<T>另一个类实例化一个对象,我们仍然无法从中获取类型参数:

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>"
    }
}

请注意,这意味着 的类型参数AnotherClass不能在 中显示SimpleClass它是类本身之外的地方!

据我了解,我们只能getGenericSuperclass()在它实际上已经知道答案的地方使用匿名子类 & 技巧。比如 in main(),这里class java.lang.String就是真正定义为 Type Argument 的地方。

(IMO,如果这个招式的能力如此受限,那它根本就没有真正的用处。)

4

1 回答 1

0

为此请查看TypeTools。例子:

List<String> stringList = new ArrayList<String>() {};
Class<?> stringType = TypeResolver.resolveRawArgument(List.class, stringList.getClass());
assert stringType == String.class;
于 2013-10-26T02:25:45.060 回答