背景(我们真的不需要担心)
这是一个源自于的问题构建具有继承性的通用树。我将这个问题作为一个单独的问题来打开,因为这不仅与树问题有关。这更多的是一个通用和类问题。
Question
为了更好地用代码来说明,我们有一个Tree
类,一个SubTree
类,以及一个WrongSubTree
class:
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 行目前没有编译错误,也没有运行时异常。)
My trial
为此,我们尝试添加一个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代码。)
Problem
但我不知道如何调用该构造函数:
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
Edit #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"
}
注意我们仍然cannot获取“java.lang.String类”inside SimpleClass
.
更重要的是,如果我们使用类型参数<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()
在它实际上已经知道答案的地方进行欺骗。比如在main()
,这里是地方class java.lang.String
实际上被定义为类型参数。
(IMO,如果这个技巧的能力受到如此限制,那么它根本就没有什么用处。)