我正在构建一个通用的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.");

}

}

你怎么看?

java构建继承树_java - 使用继承构建通用树 - 堆栈内存溢出相关推荐

  1. java poi 段落行间距_java - Apache poi XWPF Paragraph行间距 - 堆栈内存溢出

    我正在尝试使用Java / Coldfusion中的Apache Poi构建一个word文档. 到目前为止,它让我做了很多我想要的格式,除了段落的行间距. 有谁知道如何将段落设置为单行间隔? 它不断构 ...

  2. java程序包r不存在_java - 从命令行使用Gradle构建时,“程序包R不存在”错误 - 堆栈内存溢出...

    我正在尝试从命令行使用Gradle构建一个Android项目,但是当我想要更改目录结构时发现了一个问题. 目前是这样的: . └── main ├── AndroidManifest.xml ├── ...

  3. axis2 java客户端内存溢出怎么办_java - Axis2-总是出现404错误 - 堆栈内存溢出

    我试图将一些Web服务存根从Metro移到Axis2,但是在使用wsdl2java生成存根后,每次尝试进行服务调用时,我都会不断收到404错误. 16/12/2010 11:14:57 AM org. ...

  4. java从端口接收数据_java - Java中通过串行端口接收数据的效率更高 - 堆栈内存溢出...

    这是代码片段 我在这里启动一个动作监听器 try { port_seleted.addEventListener(this); } catch (TooManyListenersException e ...

  5. java获取excel文件第一行_java - 在Java中读取Excel文件,但第一行除外 - 堆栈内存溢出...

    我正在尝试从使用xlsx文件的JasperReport模板自动创建报告,我用来从excel文件读取的方法是这样的: String[] columnNames = new String[]{" ...

  6. java 获取permgen_java - Java:如何检查当前的Perm / PermGen大小? - 堆栈内存溢出

    您可以在这里使用jmap ,它是JVM堆转储工具. 例如: jmap -heap 5900 它将打印: Heap Configuration: MinHeapFreeRatio = 40 MaxHea ...

  7. java远程计算机终止,java - javax.net.ssl.SSLHandshakeException:远程主机终止了握手 - 堆栈内存溢出...

    我已经使用以下命令在Java信任库(cacerts)中安装了证书: keytool-导入-文件" C:\\ Users \\ kdursoji \\ Downloads \\ Zscaler ...

  8. android 415错误,java - Android Retrofit2无法添加标头(415错误代码) - 堆栈内存溢出...

    我正在尝试通过skyscanner api访问票证数据并将其传递到我的视图,但是我无法实现这一点,因为我收到415错误代码,我正在使用Retrofit2并以编程方式添加标头. 我的界面如下所示: pu ...

  9. java 抽象类继承抽象类_Java之继承、抽象类、接口篇

    一.继承(extends) 什么是继承? 继承是对现实生活中的"分类"概念的一种模拟. 狮子拥有动物的一切基本特性,但同时又拥有自己的独特的特性,这就是"继承" ...

  10. java 封装 继承 堕胎_Java的继承、封装和多态

    一.继承 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为. 继承的特性 子类拥有父类非 private 的属性.方法. ...

最新文章

  1. 训练Rainbow算法需要1425个GPU Day?谷歌说强化学习可以降低计算成本
  2. Android(Linux)实时监控串口数据
  3. 环境变量太大解决方法
  4. cad自动标注界址点_CAD制图中的5个小技巧
  5. Python回调函数
  6. 【Linux网络编程】TCP三次握手和四次挥手
  7. 福昕熊雨前:PDFium开源项目的背后
  8. 通用单向链表设计(一)——接口的设计
  9. ubuntu 下安装和启动SSH 服务
  10. python优雅编程_Python——traceback的优雅处理
  11. 多变量频率统计——r
  12. 【数字信号调制】基于matlab GUI数字信号调制仿真系统【含Matlab源码 336期】
  13. Redis的安装和使用之二------phpredis与phpRedisAdmin
  14. 穷建站(一):申请免费的tk顶级域名
  15. 多肽细胞穿膜肽TAT修饰牛血清白蛋白BSA/人血清白蛋白HSA/卵清白蛋白OVA纳米粒(实验要求)
  16. 解决zing生成二维码时二维码太小、白框太大的问题
  17. 广东计算机二级考试大纲,全国计算机等级考试大纲(二级)
  18. 微星主板开启安全启动以更新win11教程
  19. MVC 音乐商店 第1部分: 概述和文件- 新建项目
  20. Unity光照效果_自发光动态改变并影响环境

热门文章

  1. 计算机课程设计收费管理系统,数据结构课程设计报告---收费停车场管理系统
  2. allegro中Comps和Symbols属性的区别及各属性解释
  3. Linux内核分析(一)通过汇编代码,理解程序在计算机中是如何运行的
  4. 做3D建模师工资到底如何?
  5. mac如何通过路径找到对应的文件夹——快捷键
  6. 航空公司客户价值分析笔记
  7. Quidway Eudemon 系列防火墙增加IP访问
  8. eudemon 1000 小问题处理之一
  9. 离散制造与流程制造的区别与特点
  10. 最易懂Spring循环依赖