我正在构建一个通用的Tree类,它支持子树的继承。 但我遇到了一些问题。 请你帮帮我吗?

描述

让我们定义Tree类和BlueTree类,其中BlueTree extends Tree 。

让我们定义Leaf类和RedLeaf类,其中RedLeaf extends Leaf 。 它们被用作树包含的“数据”。

Tree表示Tree类型的Tree ,其“data”是Leaf类型。

对于继承 (这不是适当的Java继承):

Tree可以有类型的子项

Tree , Tree , BlueTree和BlueTree 。

Tree可以有类型的子项

Tree和BlueTree ,

但不是 Tree或BlueTree 。

BlueTree可以有类型的子项

BlueTree和BlueTree ,

但不是 Tree或Tree 。

BlueTree可以有类型的子项

BlueTree ,

但不是 Tree , Tree或BlueTree 。

*这里,“孩子”是指树的分支/叶子。

(有点复杂,这就是我将线分开的原因。)

代码

(如果你有一个解决方案,你可能不需要阅读下面我的尝试的详细说明。如果你想一起找到解决方案,我的代码可能会给你一些想法 - 或者,它可能会混淆它们。)

初审 :(简单的)

// This is the focus of this question, the class signature
public class Tree {
// some fields, but they are not important in this question
private Tree super T> mParent;
private T mData;
private ArrayList> 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
}
}
该类结构满足描述中的大多数要求。 除此之外,它允许
class BlueTree extends Tree { }
class Leaf { }
class RedLeaf extends Leaf { }
Tree tree_leaf = new Tree();
BlueTree blueTree_leaf = new BlueTree();
blueTree_leaf.addChild(tree_leaf); // should be forbidden
违反了
BlueTree 不能具有Tree类型的子项。
问题是因为,在BlueTree ,它的addChild()方法签名仍然存在
public void addChild(final Tree extends Leaf> subTree) {
// add the subTree to mChildren
}
理想情况是, BlueTree.addChild()方法签名被更改(在继承时自动)
public void addChild(final BlueTree extends Leaf> subTree) {
// add the subTree to mChildren
}
(注意,此方法不能通过继承覆盖上述方法,因为参数类型不同。)
有一个解决方法。 我们可以添加一个类继承检查,并为这种情况抛出RuntimeException :
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
}
但是使它成为编译时错误远比运行时错误好。 我想在编译时强制执行此行为。
二审
第一个试验结构中的问题是,方法addChild()的参数类型Tree不是泛型类型参数。 因此,它不会在继承时更新。 这一次,让我们尝试使它成为泛型类型参数。
首先,定义一般的Tree类。
public class Tree {
private Tree super T> mParent;
private T mData;
private ArrayList> mChildren;
/*package*/ void addChild(final Tree extends T> subTree) {
// add the subTree to mChildren
}
}
然后是管理Tree对象的TreeManager 。
public final class TreeManager, DataType> {
private NodeType mTree;
public TreeManager(Class 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
// is not applicable for the arguments (NodeType)
}
// for testing
public static void main(String[] args) {
@SuppressWarnings("unchecked")
TreeManager , Leaf> tm_TreeLeaf_Leaf = new TreeManager, Leaf> ((Class>) new Tree ().getClass());
TreeManager, RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager, RedLeaf>((Class>) new Tree ().getClass());
TreeManager , Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager, Leaf> ((Class>) new BlueTree ().getClass());
TreeManager, RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager, RedLeaf>((Class>) new BlueTree().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 , RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager, RedLeaf>((Class>) new Tree ().getClass());
TreeManager , RedLeaf> tm_BlueTreeLeaf_RedLeaf = new TreeManager, RedLeaf>((Class>) new BlueTree ().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, Leaf> tm_TreeRedLeaf_Leaf = new TreeManager, Leaf> ((Class>) new Tree ().getClass());
TreeManager, Leaf> tm_BlueTreeRedLeaf_Leaf = new TreeManager, Leaf> ((Class>) new BlueTree().getClass());
}
}
TreeManager初始化没有问题; 虽然线条有点长。 它也符合说明中的规则。
但是,在TreeManager调用Tree.addChild()时会出现编译错误,如上所示。
第三次审判
为了修复第二次试用中的编译错误,我尝试更改类签名(甚至更长)。 现在是mTree.addChild(subTree); 编译没有问题。
// T is not used in the class. T is act as a reference in the signature only
public class TreeManager3, DataType extends T> {
private NodeType mTree;
public TreeManager3(Class ClassNodeType) {
try {
mTree = ClassNodeType.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public void managerAddChild(final NodeType subTree) {
mTree.addChild(subTree); // compile-error is gone
}
}
我使用与第二次试验非常相似的代码对其进行了测试。 正如第二次试验所做的那样,它没有任何问题。 (甚至更长。)
(您可以跳过下面的代码块,因为它只是在逻辑上重复。)
public static void main(String[] args) {
@SuppressWarnings("unchecked")
TreeManager3 , Leaf> tm_TreeLeaf_Leaf = new TreeManager3, Leaf> ((Class>) new Tree ().getClass());
TreeManager3, RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager3, RedLeaf>((Class>) new Tree ().getClass());
TreeManager3 , Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager3, Leaf> ((Class>) new BlueTree ().getClass());
TreeManager3, RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager3, RedLeaf>((Class>) new BlueTree().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 , RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager3, RedLeaf>((Class>) new Tree ().getClass());
TreeManager3 , RedLeaf> tm_BlueTreeLeaf_RedLeaf = new TreeManager3, RedLeaf>((Class>) new BlueTree ().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, Leaf> tm_TreeRedLeaf_Leaf = new TreeManager3, Leaf> ((Class>) new Tree ().getClass());
TreeManager3, Leaf> tm_BlueTreeRedLeaf_Leaf = new TreeManager3, Leaf> ((Class>) new BlueTree().getClass());
}
但是,当我尝试调用TreeManager3.managerAddChild()时出现问题。
tm_TreeLeaf_Leaf.managerAddChild(new Tree());
tm_TreeLeaf_Leaf.managerAddChild(new Tree()); // compile error: managerAddChild(Tree) cannot cast to managerAddChild(Tree)
tm_TreeLeaf_Leaf.managerAddChild(new BlueTree());
tm_TreeLeaf_Leaf.managerAddChild(new BlueTree()); // compile error: managerAddChild(BlueTree) cannot cast to managerAddChild(BlueTree)
这是可以理解的。 TreeManager3.managerAddChild(NodeType)表示TreeManager3.managerAddChild(Tree)并且没有通配符Tree extends T> 在第一次试验中,在参数类型中Tree extends T> ,如Tree.addChild(final Tree extends T> subTree) 。
乞求你的帮助......
我已经没有想法了。 我是否朝错误的方向解决这个问题? 我花了很多时间来打理这个问题,并尽最大努力使其更具可读性,更易于理解和遵循。 我不得不说抱歉它仍然很长很冗长。 但是,如果你知道方式,请你帮忙,或者请给我任何想法? 您的每一个输入都非常感谢。 非常感谢!
编辑#1(以下评论 )
总部设在一审判决后 ,只允许mChildren被修改addChild()等方法与isAssignableFrom()检查),因此即使允许用户继承Tree和压倒一切addChild()不会打破树的完整性。
/developer/util/Tree.java
package developer.util;
import java.util.ArrayList;
public class Tree {
private Tree super T> mParent;
private final ArrayList> mChildren = new ArrayList>();
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 extends Tree {
@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 treeOfInt = new Tree();
BinaryTree btreeOfInt = new BinaryTree();
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.");
}
}

你怎么看?