Java 通用类型x通用参数:建筑a";“非常通用”;结构 目标
例如,我们有一个Java 通用类型x通用参数:建筑a";“非常通用”;结构 目标,java,generics,methods,arraylist,generic-programming,Java,Generics,Methods,Arraylist,Generic Programming,例如,我们有一个树类,类型为T:即树 我们想让这个树类能够容纳 树(当然) 子树其中子树扩展了树 树其中SubT扩展了T,以及 子树其中子树扩展树和子树扩展T “Hold”表示接受某个子类,并根据请求分别返回某个子类的对象 例如,原始的ArrayList具有以下属性: private static class Leaf { } private static class RedLeaf extends Leaf { } @Test public final void test() { Ar
树
类,类型为T
:即树
我们想让这个树
类能够容纳
树
(当然)子树
其中子树扩展了树
树
其中SubT扩展了T
,以及子树
其中子树扩展树
和子树扩展T
ArrayList
具有以下属性:
private static class Leaf {
}
private static class RedLeaf extends Leaf {
}
@Test
public final void test() {
ArrayList<Leaf> al = new ArrayList<Leaf>();
al.add(new Leaf());
System.out.println(al.get(al.size()-1).getClass()); // class Leaf
al.add(new RedLeaf());
System.out.println(al.get(al.size()-1).getClass()); // class RedLeaf
}
为什么这是一场灾难?考虑树中的某个节点,我们希望找到下一个兄弟。
事实上,我们可以在插入元素时进行快速修复:
private static class Leaf {
Leaf() { super(); }
Leaf(Leaf leaf) { super(); }
}
private static class RedLeaf extends Leaf {
RedLeaf() { super(); }
RedLeaf(RedLeaf redLeaf) { super(redLeaf); }
}
@Test
public final void test() {
ArrayList<Leaf> al = new ArrayList<Leaf>();
Leaf leaf = new Leaf();
RedLeaf redLeaf = new RedLeaf();
al.add(new Leaf(leaf));
al.add(new RedLeaf(redLeaf));
al.add(new Leaf(leaf));
System.out.println(al.indexOf(al.get( 0 ))); // 0
System.out.println(al.indexOf(al.get( 1 ))); // 1
System.out.println(al.indexOf(al.get( 2 ))); // 2 <-- nice :-)
}
我们有这个addChild
方法,没有问题:
public void addChild(final Tree<T> subTree) {
getChildren().add(new Tree<T>(this, subTree)); // copy the tree & set parent attach to this
}
在概念上,我们希望将addChild
方法修改为:
(但是下面的代码有编译错误,如注释所示。)
返回基类,树
。下面是addChild
存储对象的方式
public Tree<T> addChildren(final Tree<? extends T>... subTrees) {
if (subTrees == null) // called addChildren((Tree<T>) null)
addChild((Tree<T>) null); // add null to children
else
for (final Tree<? extends T> subTree : subTrees) // empty parameter goes here != null array
addChild(subTree);
return this;
}
public Tree<T> addChild(final Tree<? extends T> subTree) {
if (subTree == null) // for addChild((Tree<T>) null)
getChildren().add(null); // add null to children
else { // else
getChildren().add( // copy (constructor) the tree & set parent attach to this
Reflection.<Tree<T>>invokeConstructor(subTree, new ParameterTypeAndArg(subTree.getClass(), subTree))
.setParent(this));
}
return this;
}
public Tree addChildren(final Tree首先,您将此操作复杂化了。您真正需要做的是:
public void add(final Tree<? extends T> subTree) {
如果Leaf
是A
并且子树是树
,则添加(最终子树)
与树
不匹配
因此,从概念上讲,您实际上希望:
public <Leaf extends T, SubTree extends Tree<Leaf>> void add(final SubTree<Leaf> subTree) {
然而,现在你有一个更大的问题,这是一个经典的问题,在这里的许多其他地方都有答案:
tr = new SubTree();
由于类型擦除,您无法实例化泛型类型的对象。您必须在某个位置指定子树的类
,然后使用.newInstance()
对其进行实例化。您可以向我们显示更多代码吗?构造函数等是什么?public void add(最终树)“编译器错误”节看起来像Scala中的Scala=D,我们可以编写使用泛型类型参数的函数。谢谢!回答得非常好,有很好的解释!我有几个问题。如果我使用你不能单独从泛型类型实例化对象。句号。你必须有对象的类可用。然后你可以使用Class、 newInstance()
使用默认构造函数实例化该类型的对象,也可以使用Class.getConstructor(…)
获取特定的非默认构造函数。是否要复制值或存储引用或其他内容完全取决于您的应用程序要求。如果需要复制,则必须复制。如果不需要复制,则不必复制。执行需要执行的操作。:)此外,您永远不需要将子树
显式强制转换为树
。您可以始终将子树
存储在一个变量中,该变量可以容纳树
,而无需任何显式强制转换,因为子树
扩展了树
。您需要显式强制转换相反的内容(除非你知道树
是一个子树
),否则该转换可能会失败。无论如何,Class.newInstance()
的返回类型是T
。请看。非常感谢!!根据你的回答和解释,我学到了很多,并构建了一个非常通用的树,它可以接受和保持(而不是重新创建超级类)子树、树、子树的节点。我使用默认构造函数来确保所有子类定义一个副本构造函数。这样,在插入时,我可以安全地使用反射来调用子类的副本构造函数。代码发布在上面。请随意更正我。我希望它也能帮助其他人:-)
/** Default constructor. **/
public Tree() { // All sub-classes instantiation must invoke this default constructor
super(); // here is a good place to ensure every sub-class has a copy constructor
if (Reflection.hasCopyConstructor(this) == false)
throw new CopyConstructorRequiredException(this.getClass());
}
class Reflection {
public static boolean hasCopyConstructor(final Object object) {
return hasCopyConstructor(object.getClass());
}
public static boolean hasCopyConstructor(final Class<?> clazz) {
try {
clazz.getDeclaredConstructor(clazz);
return true;
} catch (SecurityException e) {
e.printStackTrace();
return false;
} catch (NoSuchMethodException e) {
e.printStackTrace();
return false;
}
}
}
private Tree(final Tree<? extends T> copyFrom) {
super();
if (copyFrom != null) {
this.setData(copyFrom.getData());
for (final Tree<? extends T> child : copyFrom.getChildren()) {
this.addChildren(child); // addChildren() handles null well
}
}
}
private static class BlueTree<T> extends Tree<T> {
private BlueTree(final BlueTree<T> blueTree) { super(blueTree); }
}
public Tree<T> addChildren(final Tree<? extends T>... subTrees) {
if (subTrees == null) // called addChildren((Tree<T>) null)
addChild((Tree<T>) null); // add null to children
else
for (final Tree<? extends T> subTree : subTrees) // empty parameter goes here != null array
addChild(subTree);
return this;
}
public Tree<T> addChild(final Tree<? extends T> subTree) {
if (subTree == null) // for addChild((Tree<T>) null)
getChildren().add(null); // add null to children
else { // else
getChildren().add( // copy (constructor) the tree & set parent attach to this
Reflection.<Tree<T>>invokeConstructor(subTree, new ParameterTypeAndArg(subTree.getClass(), subTree))
.setParent(this));
}
return this;
}
public void add(final Tree<? extends T> subTree) {
public class Base { }
public class A extends Base { }
public class B extends Base { }
public <Leaf extends T, SubTree extends Tree<Leaf>> void add(final SubTree<Leaf> subTree) {
public <Leaf extends T, SubTree extends Tree<Leaf>> void add(final SubTree subTree) {
{
Tree<Object> x = new Tree<Object>();
MyTree<Integer> y = new MyTree<Integer>();
Tree<Integer> z = new Tree<Integer>();
x.add(y);
y.add(x); // not valid, as Tree<Object> does not extend Tree<Integer>
y.add(z); // fine, as Tree<Integer> matches
}
public static class MyTree<T> extends Tree<T> {
}
SubTree tr = ...;
tr = new SubTree();