问题描述
我构建了一个支持继承子树的通用 $ b code> class和 BlueTree 类,其中 BlueTree扩展树。
让我们定义 Leaf 类和 RedLeaf 类,其中 RedLeaf扩展Leaf 。它们被用作树所包含的数据。
A 树< Leaf> Tree ,其数据类型为 Leaf 。
继承(这是不是适当的Java继承): 一个树< Leaf> 可以有类型为
- 的树的孩子< Leaf> ; , Tree< RedLeaf> , BlueTree< Leaf> 和 BlueTree< RedLeaf> 。
- a Tree< RedLeaf> 可以包含
- 树< RedLeaf> 和 BlueTree< RedLeaf> ,
- 但不是 树< Leaf> 或 BlueTree< Leaf> / code>。
。
- a BlueTree< Leaf> 可以有ch类型
- BlueTree< Leaf> 和 BlueTree< RedLeaf> ,
- 但不是 树< Leaf> 或 Tree< RedLeaf> 。
。 - a BlueTree< RedLeaf> 可以包含
类型的子项
- BlueTree< RedLeaf> ,
- strong> Tree< Leaf> , Tree< RedLeaf> 或 BlueTree< Leaf> / code>。
$ b
(有点复杂,这就是为什么我将行分开的原因。)
代码
(如果您有解决方案,您可能不需要阅读下面的详细说明。如果你想一起找出解决方案,我的代码可能会给你一些想法 - 或者,它可能会混淆他们。)
:(简单的)//这个问题的焦点是类签名
公开课Tree< T> {
//一些字段,但在这个问题中它们并不重要
私有树<超T> mParent;
private T mData;
private ArrayList<树<延伸T>> mChildren;
//这是这个问题的焦点,addChild()方法签名
public void addChild(final Tree< ;? extends T> subTree){
// // add subTree to mChildren
}
}
这个类结构满足大多数需求在描述中。除此之外,它允许
class BlueTree< T>延伸Tree< T> {}
class Leaf {}
class RedLeaf extends Leaf {}
Tree< Leaf> tree_leaf = new Tree< Leaf>();
BlueTree< Leaf> blueTree_leaf = new BlueTree< Leaf>();
blueTree_leaf.addChild(tree_leaf); //应该被禁止
违反了
- a BlueTree< Leaf> 不能的子类型为 Tree< Leaf> 。
问题是因为在 BlueTree< Leaf> ,它的 addChild()方法签名仍然是
public void addChild(final Tree< ;? extends Leaf> subTree){
//将子树添加到mChildren
}
理想的情况是, BlueTree< Leaf> .addChild()方法签名被改变(自动地继承)到
public void addChild(final BlueTree< ;? extends Leaf> subTree){
//将子树添加到mChildren
} $ b
$ b(请注意,此方法不能通过继承覆盖上述方法,因为参数类型不同。)
有解决方法。我们可以添加一个类继承检查,并为这种情况抛出 RuntimeException :
public void addChild(final Tree< ;? extends Leaf> subTree){
if(this.getClass()。isAssignableFrom(subTree.getClass()))
throw new RuntimeException(参数是无效的类。);
//将子树添加到mChildren
}
但是使它成为一个编译时间错误远远好于运行时错误。我想在编译时强制执行此行为。
第二次试用
第一个试验结构中的问题是,方法 addChild()中的参数类型 Tree 不是通用的类型参数。因此它不会在继承时更新。首先,定义一般的树类。然后,让我们尝试使它成为一个泛型类型参数。
public class Tree< T> {
私人树<超T> mParent;
private T mData;
private ArrayList<树<延伸T>> mChildren;
$ b $ * package * / void addChild(final Tree< ;? extends T> subTree){
//将子树添加到mChildren
}
}
然后管理的 TreeManager > Tree object。
public final class TreeManager< NodeType extends Tree<超级数据类型>,数据类型> {
私人NodeType mTree;
public TreeManager(Class< NodeType> ClassNodeType){
try {
mTree = ClassNodeType.newInstance();
} catch(Exception e){
e.printStackTrace();
}
}
public void managerAddChild(final NodeType子树){
mTree.addChild(subTree);
//编译错误:类型树中的方法addChild(Tree< ;?扩展捕获#1 of-super DataType>)
// //捕获#超级数据类型>
//不适用于参数(NodeType)
}
//用于测试
public static void main(String [] args){
@SuppressWarnings(unchecked)
TreeManager< Tree< Leaf> ,Leaf> tm_TreeLeaf_Leaf =新的TreeManager<树< Leaf>,叶> ((Class< Tree< Leaf>>)new Tree< Leaf>().getClass());
TreeManager< Tree< RedLeaf>,RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager< Tree< RedLeaf>,RedLeaf>((Class< Tree< RedLeaf>>)new Tree< RedLeaf>()。getClass());
TreeManager< BlueTree< Leaf> ,Leaf> tm_BlueTreeLeaf_Leaf =新的TreeManager< BlueTree< Leaf>叶> ((Class TreeManager< BlueTree< RedLeaf>,RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager< BlueTree< RedLeaf>,RedLeaf>((Class< BlueTree< RedLeaf>)new BlueTree< RedLeaf>().getClass());
System.out.println(tm_TreeLeaf_Leaf .mTree.getClass()); // class Tree
System.out.println(tm_TreeRedLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_Leaf .mTree.getClass()); // class BlueTree
System.out.println(tm_BlueTreeRedLeaf_RedLeaf.mTree.getClass()); // class BlueTree
@SuppressWarnings(unchecked)
TreeManager< Tree< Leaf> ,RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager< Tree< Leaf>,RedLeaf>((Class< Tree< Leaf>)new Tree< Leaf>().getClass());
TreeManager< BlueTree< Leaf> ,RedLeaf> tm_BlueTreeLeaf_RedLeaf =新的TreeManager< BlueTree< Leaf>,RedLeaf>((Class< BlueTree< Leaf>)新的BlueTree< Leaf>().getClass());
System.out.println(tm_TreeLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_RedLeaf .mTree.getClass()); // class BlueTree
//以下两个有编译错误,这是很好的和预期的。
TreeManager< Tree< RedLeaf>,Leaf> tm_TreeRedLeaf_Leaf =新的TreeManager<树< RedLeaf>,叶> ((Class< Tree< RedLeaf>>)new Tree< RedLeaf>()。getClass());
TreeManager< BlueTree< RedLeaf>,Leaf> tm_BlueTreeRedLeaf_Leaf =新的TreeManager< BlueTree< RedLeaf>,叶> ((Class< BlueTree< RedLeaf>>)new BlueTree< RedLeaf>()。getClass());
TreeManager 没有问题的初始化;线条虽然有点长。它也符合描述中的规则。
然而,调用 Tree.addChild()$ c时会出现编译错误$ c>在> TreeManager 中,如上所示。 第三个试用
为了解决第二个试验中的编译错误,我尝试改变类签名(甚至更长)。现在 mTree.addChild(subTree); 编译时没有问题。
// T不在课堂中使用。 T用作仅在签名中的引用
public class TreeManager3< T,NodeType extends Tree< T>,DataType extends T> {
私人NodeType mTree;
public TreeManager3(Class< NodeType> ClassNodeType){
try {
mTree = ClassNodeType.newInstance();
} catch(Exception e){
e.printStackTrace();
}
}
public void managerAddChild(final NodeType子树){
mTree.addChild(subTree); //编译错误消失
}
}
我测试过它与第二次审判代码非常相似。它的创建没有任何问题,就像第二次审判一样。 (甚至更长。)
(您可以跳过下面的代码块,因为它只是在逻辑上重复。)
public static void main(String [] args){
@SuppressWarnings(unchecked)
TreeManager3< Leaf,Tree <叶> ,Leaf> tm_TreeLeaf_Leaf = new TreeManager3< Leaf,Tree< Leaf> Leaf> ((Class< Tree< Leaf>>)new Tree< Leaf>().getClass());
TreeManager3< RedLeaf,树< RedLeaf>,RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager3< RedLeaf,Tree< RedLeaf>,RedLeaf>((Class< Tree< RedLeaf>)new Tree< RedLeaf>()。getClass());
TreeManager3< Leaf,BlueTree< Leaf> ,Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager3< Leaf,BlueTree< Leaf> Leaf> ((Class TreeManager3< RedLeaf,BlueTree< RedLeaf>,RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager3< RedLeaf,BlueTree< RedLeaf>,RedLeaf>((Class< BlueTree< RedLeaf>)new BlueTree< RedLeaf>()。getClass());
System.out.println(tm_TreeLeaf_Leaf .mTree.getClass()); // class Tree
System.out.println(tm_TreeRedLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_Leaf .mTree.getClass()); // class BlueTree
System.out.println(tm_BlueTreeRedLeaf_RedLeaf.mTree.getClass()); // class BlueTree
@SuppressWarnings(unchecked)
TreeManager3< Leaf,Tree< Leaf> ,RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager3< Leaf,Tree< Leaf>,RedLeaf>((Class< Tree< Leaf>)new Tree< Leaf>().getClass());
TreeManager3< Leaf,BlueTree< Leaf> ,RedLeaf>新的TreeManager3< Leaf,BlueTree< Leaf>,RedLeaf>((Class< BlueTree< Leaf>)new BlueTree< Leaf>().getClass());
System.out.println(tm_TreeLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_RedLeaf .mTree.getClass()); // class BlueTree
//以下两个有编译错误,这是很好的和预期的。
TreeManager3< RedLeaf,Tree< RedLeaf>,Leaf> tm_TreeRedLeaf_Leaf = new TreeManager3< RedLeaf,Tree< RedLeaf>,Leaf> ((Class< Tree< RedLeaf>>)new Tree< RedLeaf>()。getClass());
TreeManager3< RedLeaf,BlueTree< RedLeaf>,Leaf> tm_BlueTreeRedLeaf_Leaf =新的TreeManager3< RedLeaf,BlueTree< RedLeaf>,Leaf> ((Class< BlueTree< RedLeaf>>)new BlueTree< RedLeaf>()。getClass());
}
然而,当我尝试调用时出现问题, TreeManager3.managerAddChild()。
tm_TreeLeaf_Leaf.managerAddChild(new Tree< Leaf>
tm_TreeLeaf_Leaf.managerAddChild(new Tree< RedLeaf>()); //编译错误:managerAddChild(Tree< RedLeaf>)无法转换到managerAddChild(Tree< Leaf>)
tm_TreeLeaf_Leaf.managerAddChild(new BlueTree< Leaf>);
tm_TreeLeaf_Leaf.managerAddChild(新的BlueTree< RedLeaf>()); //编译错误:managerAddChild(BlueTree< RedLeaf>)无法转换为managerAddChild(BlueTree< Leaf>)
这是可以理解的。 TreeManager3.managerAddChild(NodeType)表示 TreeManager3.managerAddChild(树< T>)并且没有通配符树< ;?在参数类型中扩展T> ,如在第一次试验中 Tree.addChild(最终树<扩展T>子树)。 >
乞求您的帮助...
我已经用完了想法。我是否在错误的方向去解决这个问题?我花了很多时间打出这个问题,并尽了最大的努力使它更易读,更容易理解,并遵循。我不得不说,它仍然是非常漫长而冗长的。但是,如果你知道方式,可以请你帮忙,还是请给我一些你的想法?您的每一个输入是高度赞赏。非常感谢!
编辑#1(用于)
基于第一次试用版,只允许 mChildren 由 addChild()(以及其他方法使用 isAssignableFrom() check),所以即使允许用户继承 Tree 并覆盖 addChild()不会破坏树的完整性。
/developer/util/Tree.java
package developer.util;
import java.util.ArrayList;
public class Tree< T> {
私有树<超T> mParent;
private final ArrayList< Tree<延伸T>> mChildren =新的ArrayList<树<扩展T>>();
public int getChildCount(){return mChildren.size(); }
公共树 $ b $ public void addChild(final Tree< ;? extends T> subTree){
if(this.getClass()。isAssignableFrom(subTree.getClass())== false)
抛出新的RuntimeException(child(subTree)必须是此树的子类。);
subTree.mParent = this;
mChildren.add(subTree);
}
}
/user/pkg/BinaryTree.java
package user.pkg;
导入developer.util.Tree;
公共类BinaryTree< T>延伸Tree< T> {
@Override
public void addChild(final Tree< ;? extends T> subTree){
if(getChildCount()< 2){
super.addChild(subTree);
}
}
}
/ Main。 java
import user.pkg.BinaryTree;
导入developer.util.Tree;
public class Main {
public static void main(String [] args){
Tree< Integer> treeOfInt = new Tree< Integer>();
BinaryTree<整数> btreeOfInt = new BinaryTree< Integer>();
treeOfInt.addChild(btreeOfInt);
System.out.println(treeOfInt.getLastChild()。getClass());
// class user.pkg.BinaryTree
try {
btreeOfInt.addChild(treeOfInt);
} catch(Exception e){
System.out.println(e);
// java.lang.RuntimeException:子(子树)必须是此树的子类。
}
System.out.println(done。);
}
}
您怎么看?
解决方案正如我所见,这个问题没有完美的解决方案。这基本上是由于类型擦除。 文章解释说,您的 addChild(final Tree<?extends Leaf> subTree)函数将成为 addChild(最终树子树)函数。因此,即使您可以以某种方式使用通用参数< TreeType extends Tree<延伸叶>> addChild(final TreeType subTree)(无效的语法!)它将在编译时被擦除为 addChild(最终Tree子树)。添加你的运行时测试可以工作,所以你做的编辑将会完成这项工作。
I am building a generic Tree<T> class, which supports inheritance of sub-trees. But I've encountered some problems. Would you please kindly help me?
Description
Let's define the Tree class and the BlueTree class, where BlueTree extends Tree.
Let's define the Leaf class and the RedLeaf class, where RedLeaf extends Leaf. They are used as the "data" the Trees contain.
A Tree<Leaf> means a Tree of type Tree, and its "data" is of type Leaf.
For inheritance (this is not proper Java inheritance):
- a Tree<Leaf> can have child of type
- Tree<Leaf>, Tree<RedLeaf>, BlueTree<Leaf>, and BlueTree<RedLeaf>.
.
- a Tree<RedLeaf> can have child of type
- Tree<RedLeaf>, and BlueTree<RedLeaf>,
- but not Tree<Leaf>, or BlueTree<Leaf>.
.
- a BlueTree<Leaf> can have child of type
- BlueTree<Leaf>, and BlueTree<RedLeaf>,
- but not Tree<Leaf>, or Tree<RedLeaf>.
.
- a BlueTree<RedLeaf> can have child of type
- BlueTree<RedLeaf>,
- but not Tree<Leaf>, Tree<RedLeaf>, or BlueTree<Leaf>.
*Here, "child" means the branches / leaves of the Tree.
(a bit complicated, that's why I separate the lines.)
The code
(If you have a solution, you may not need to read the verbose illustration of my attempts below. If you wish to find out the solution together, my code may give you some ideas - or, it may confuse them.)
First Trial: (the simple one)
// This is the focus of this question, the class signature public class Tree<T> { // some fields, but they are not important in this question private Tree<? super T> mParent; private T mData; private ArrayList<Tree<? extends T>> mChildren; // This is the focus of this question, the addChild() method signature public void addChild(final Tree<? extends T> subTree) { // add the subTree to mChildren } }
This class structure meets most of the requirements in the description. Except, it allows
class BlueTree<T> extends Tree<T> { } class Leaf { } class RedLeaf extends Leaf { } Tree<Leaf> tree_leaf = new Tree<Leaf>(); BlueTree<Leaf> blueTree_leaf = new BlueTree<Leaf>(); blueTree_leaf.addChild(tree_leaf); // should be forbidden
which violates
- a BlueTree<Leaf> cannot have child of type Tree<Leaf>.
The problem is because, in BlueTree<Leaf>, its addChild() method signature is still
public void addChild(final Tree<? extends Leaf> subTree) { // add the subTree to mChildren }
The ideal case is, the BlueTree<Leaf>.addChild() method signature is changed (automatically, upon inheritance) to
public void addChild(final BlueTree<? extends Leaf> subTree) { // add the subTree to mChildren }
(Note that this method cannot override the above method by inheritance, as the parameter types differ.)
There is a workaround. We may add a class inheritance check, and throw RuntimeException for this case:
public void addChild(final Tree<? extends Leaf> subTree) { if (this.getClass().isAssignableFrom(subTree.getClass())) throw new RuntimeException("The parameter is of invalid class."); // add the subTree to mChildren }
But making it a compile-time error is far better than a run-time error. I would like to enforce this behaviour at compile-time.
Second Trial
The problem in the first trial structure is, the parameter type Tree in the method addChild() is not a generic type parameter. Thus it will not be updated upon inheritance. This time, let's try to make it a generic type parameter also.
Firstly, define the general Tree class.
public class Tree<T> { private Tree<? super T> mParent; private T mData; private ArrayList<Tree<? extends T>> mChildren; /*package*/ void addChild(final Tree<? extends T> subTree) { // add the subTree to mChildren } }
Then the TreeManager which manages a Tree object.
public final class TreeManager<NodeType extends Tree<? super DataType>, DataType> { private NodeType mTree; public TreeManager(Class<NodeType> ClassNodeType) { try { mTree = ClassNodeType.newInstance(); } catch (Exception e) { e.printStackTrace(); } } public void managerAddChild(final NodeType subTree) { mTree.addChild(subTree); // compile error: The method addChild(Tree<? extends capture#1-of ? super DataType>) // in the type Tree<capture#1-of ? super DataType> // is not applicable for the arguments (NodeType) } // for testing public static void main(String[] args) { @SuppressWarnings("unchecked") TreeManager<Tree <Leaf> , Leaf> tm_TreeLeaf_Leaf = new TreeManager<Tree <Leaf>, Leaf> ((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass()); TreeManager<Tree <RedLeaf>, RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager<Tree <RedLeaf>, RedLeaf>((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass()); TreeManager<BlueTree<Leaf> , Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager<BlueTree<Leaf>, Leaf> ((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass()); TreeManager<BlueTree<RedLeaf>, RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager<BlueTree<RedLeaf>, RedLeaf>((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass()); System.out.println(tm_TreeLeaf_Leaf .mTree.getClass()); // class Tree System.out.println(tm_TreeRedLeaf_RedLeaf .mTree.getClass()); // class Tree System.out.println(tm_BlueTreeLeaf_Leaf .mTree.getClass()); // class BlueTree System.out.println(tm_BlueTreeRedLeaf_RedLeaf.mTree.getClass()); // class BlueTree @SuppressWarnings("unchecked") TreeManager<Tree <Leaf> , RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager<Tree <Leaf>, RedLeaf>((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass()); TreeManager<BlueTree<Leaf> , RedLeaf> tm_BlueTreeLeaf_RedLeaf = new TreeManager<BlueTree<Leaf>, RedLeaf>((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass()); System.out.println(tm_TreeLeaf_RedLeaf .mTree.getClass()); // class Tree System.out.println(tm_BlueTreeLeaf_RedLeaf .mTree.getClass()); // class BlueTree // the following two have compile errors, which is good and expected. TreeManager<Tree <RedLeaf>, Leaf> tm_TreeRedLeaf_Leaf = new TreeManager<Tree <RedLeaf>, Leaf> ((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass()); TreeManager<BlueTree<RedLeaf>, Leaf> tm_BlueTreeRedLeaf_Leaf = new TreeManager<BlueTree<RedLeaf>, Leaf> ((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass()); } }
The TreeManager initialises with no problems; the lines are a bit long though. It conforms to the rules in the description also.
However, there is a compile-error when calling Tree.addChild() inside TreeManager, as illustrated above.
Third Trial
To fix the compile-error in the second trial, I tried changing the class signature (to even longer). Now mTree.addChild(subTree); compiles with no problems.
// T is not used in the class. T is act as a reference in the signature only public class TreeManager3<T, NodeType extends Tree<T>, DataType extends T> { private NodeType mTree; public TreeManager3(Class<NodeType> ClassNodeType) { try { mTree = ClassNodeType.newInstance(); } catch (Exception e) { e.printStackTrace(); } } public void managerAddChild(final NodeType subTree) { mTree.addChild(subTree); // compile-error is gone } }
And I have tested it with very similar code as to the second trial. It creates without any problems, as the second trial does. (Just even longer.)
(You may skip the code block below, as it is just logically repeating.)
public static void main(String[] args) { @SuppressWarnings("unchecked") TreeManager3<Leaf , Tree <Leaf> , Leaf> tm_TreeLeaf_Leaf = new TreeManager3<Leaf , Tree <Leaf>, Leaf> ((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass()); TreeManager3<RedLeaf, Tree <RedLeaf>, RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager3<RedLeaf, Tree <RedLeaf>, RedLeaf>((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass()); TreeManager3<Leaf , BlueTree<Leaf> , Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager3<Leaf , BlueTree<Leaf>, Leaf> ((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass()); TreeManager3<RedLeaf, BlueTree<RedLeaf>, RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager3<RedLeaf, BlueTree<RedLeaf>, RedLeaf>((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass()); System.out.println(tm_TreeLeaf_Leaf .mTree.getClass()); // class Tree System.out.println(tm_TreeRedLeaf_RedLeaf .mTree.getClass()); // class Tree System.out.println(tm_BlueTreeLeaf_Leaf .mTree.getClass()); // class BlueTree System.out.println(tm_BlueTreeRedLeaf_RedLeaf.mTree.getClass()); // class BlueTree @SuppressWarnings("unchecked") TreeManager3<Leaf , Tree <Leaf> , RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager3<Leaf , Tree <Leaf>, RedLeaf>((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass()); TreeManager3<Leaf , BlueTree<Leaf> , RedLeaf> tm_BlueTreeLeaf_RedLeaf = new TreeManager3<Leaf , BlueTree<Leaf>, RedLeaf>((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass()); System.out.println(tm_TreeLeaf_RedLeaf .mTree.getClass()); // class Tree System.out.println(tm_BlueTreeLeaf_RedLeaf .mTree.getClass()); // class BlueTree // the following two have compile errors, which is good and expected. TreeManager3<RedLeaf, Tree <RedLeaf>, Leaf> tm_TreeRedLeaf_Leaf = new TreeManager3<RedLeaf, Tree <RedLeaf>, Leaf> ((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass()); TreeManager3<RedLeaf, BlueTree<RedLeaf>, Leaf> tm_BlueTreeRedLeaf_Leaf = new TreeManager3<RedLeaf, BlueTree<RedLeaf>, Leaf> ((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass()); }
However, a problem arises when I try to call TreeManager3.managerAddChild().
tm_TreeLeaf_Leaf.managerAddChild(new Tree<Leaf>()); tm_TreeLeaf_Leaf.managerAddChild(new Tree<RedLeaf>()); // compile error: managerAddChild(Tree<RedLeaf>) cannot cast to managerAddChild(Tree<Leaf>) tm_TreeLeaf_Leaf.managerAddChild(new BlueTree<Leaf>()); tm_TreeLeaf_Leaf.managerAddChild(new BlueTree<RedLeaf>()); // compile error: managerAddChild(BlueTree<RedLeaf>) cannot cast to managerAddChild(BlueTree<Leaf>)
This is understandable. TreeManager3.managerAddChild(NodeType) means TreeManager3.managerAddChild(Tree<T>) and there is no wildcard Tree<? extends T> in the parameter type, like Tree.addChild(final Tree<? extends T> subTree) in the first trial.
Begging for your help ...
I have already run out of ideas. Was I going in the wrong direction to solve this problem? I have spent a lot of time typing up this question and tried my greatest effort to make it more readable, easier to understand, and to follow. I have to say sorry that it is still very long and verbose. But could you please help if you know the way, or please give me any ideas you have? Your every input is highly appreciated. Thanks a lot!
Edit #1 (for the comment below)
Based in the First Trial, only allow mChildren to be modified by addChild() (and other methods with the isAssignableFrom() check), so even allowing user inheritance of Tree and overriding addChild() will not break the Tree integrity.
/developer/util/Tree.java
package developer.util; import java.util.ArrayList; public class Tree<T> { private Tree<? super T> mParent; private final ArrayList<Tree<? extends T>> mChildren = new ArrayList<Tree<? extends T>>(); public int getChildCount() { return mChildren.size(); } public Tree<? extends T> getLastChild() { return mChildren.get(getChildCount()-1); } public void addChild(final Tree<? extends T> subTree) { if (this.getClass().isAssignableFrom(subTree.getClass()) == false) throw new RuntimeException("The child (subTree) must be a sub-class of this Tree."); subTree.mParent = this; mChildren.add(subTree); } }
/user/pkg/BinaryTree.java
package user.pkg; import developer.util.Tree; public class BinaryTree<T> extends Tree<T> { @Override public void addChild(final Tree<? extends T> subTree) { if (getChildCount() < 2) { super.addChild(subTree); } } }
/Main.java
import user.pkg.BinaryTree; import developer.util.Tree; public class Main { public static void main(String[] args) { Tree<Integer> treeOfInt = new Tree<Integer>(); BinaryTree<Integer> btreeOfInt = new BinaryTree<Integer>(); treeOfInt.addChild(btreeOfInt); System.out.println(treeOfInt.getLastChild().getClass()); // class user.pkg.BinaryTree try { btreeOfInt.addChild(treeOfInt); } catch (Exception e) { System.out.println(e); // java.lang.RuntimeException: The child (subTree) must be a sub-class of this Tree. } System.out.println("done."); } }
What do you think?
解决方案As I see it, there is no perfect solution to this problem. This is basically due to type erasure. The Erasure of Generic Methods article explains that your addChild(final Tree<? extends Leaf> subTree) function will become a addChild(final Tree subTree) function. So, even if you could somehow have a generic parameter <TreeType extends Tree<? extends Leaf>> addChild(final TreeType subTree) (not valid syntax!) it would be erased to addChild(final Tree subTree) at compile time. Adding your runtime test will work though, so the edit you made will do the job.
这篇关于构建具有继承性的通用树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- a Tree<Leaf> can have child of type