【问题标题】:Java: How do I cast a cloned object to it's original subclass?Java:如何将克隆的对象转换为它的原始子类?
【发布时间】:2019-12-16 19:52:06
【问题描述】:

想象一下超A类:

public class A {
    private int x;

    A(int x){
        this.x = x;
    }

    public int getX() {
        return x;
    }
}

它是 2 个子类 B 和 C:

public class B extends A{
    private int y = 2;

    B(int x){
        super(x);
    }
}
public class C extends A{
    private int y = 3;

    B(int x){
        super(x);
    }
}

现在想象一个所有继承类 A 的对象列表。我想遍历该列表并克隆每个对象,并将其转换为它的原始子类(B 或 C)。这就是我尝试这样做的方式:

public static void main(String args[]) {
    ArrayList<A> aList = new ArrayList<A>();
    aList.add(new B(5));
    aList.add(new C(6));

    ArrayList<A> aClones = new ArrayList<A>();

    for(A a : aList) {
        A aNew = new A(a.getX());
        a.getClass().cast(aNew);
        aClones.add(aNew);
    }
}

但这给了我一个错误,说我不能将 A 转换为 B。如果不事先知道我正在处理的对象的子类型,我怎么能做到这一点?我可以在 for 循环中为每种类型的子类做一个 if 语句来检查 a 是否是该子类的一个实例,然后简单地创建该特定子类的一个新实例并以这种方式克隆它。但是,在我的情况下,将有更多的子类,而不仅仅是 B 和 C,而且可能还会出现更多。我不希望 for 循环包含有关 A 的现有子类的任何知识。这可能吗?

【问题讨论】:

    标签: java casting clone cloning


    【解决方案1】:

    这不能以您编写的方式完成。

    原因很简单:BCAis-a 关系,但 A 不共享相同的 is-a BC 的关系。

    这主要是因为您尝试使用超类进行迭代来描述所有内容。

    for(A a : aList) { }
    

    在您进行迭代时,您无法通过类型推断单独知道A 实际是什么具体类。

    所以这正如预期的那样失败了。

     a.getClass().cast(aNew);
    

    如果不明确地询问类它是什么类型,你就无法做到这一点。

    这意味着如果您打算对此进行扩展,您将编写大量 instanceof 语句。

    for(A a : aList) {
        if(a instanceof B) {
            B b = new B(a.getX());
            aClones.add(b);
        }
        if(a instanceof C) {
            C c = new C(a.getX());
            aClones.add(c);
        }
    }
    

    【讨论】:

    • 好吧,那太糟糕了。我希望像 A newA = a.getClass().newInstance() 这样的东西可以工作,但由于某种原因,我对 newA 所做的任何更改都没有保存?
    • @Tipsi:我想我应该说实话。我从来没有想在Java 中做到这一点。我想不出这样做的正当理由,因为子类包含无法封装到单个对象中的更有意义的信息是非常非常罕见的。必须使用instanceof 的事实让人觉得很痛,也许这可能不是最好的方法,但如果你坚持的话,它仍然是一种选择。我会鼓励你想想你真正想要在这里完成什么。也许对象映射器会更合适。
    • 基本上,ShapeObject 是一个超类,它的所有子类都返回一个 Shape。 RectangleObject 返回一个 Rectangle 等。子类对其超类的唯一信息是 Shape 类型。所有 ShapeObject 都有一个从 XML 生成的 Properties 对象。当您将 ShapeObject 转换为 RectangleObject 时,您所做的只是让对象知道它应该从生成的 XML 属性中绘制一个 Rectangle,因此您永远不会丢失信息。
    【解决方案2】:

    你可以这样做

    for(A a : aList) {
        A anew;
        if (a.getClass() == A.class) anow = new A(a.getX());
        else if (a.getClass() == B.class) anow = new B(a.getX());
        else if (...
    

    }

    【讨论】:

    • 是的,在我的问题结束时,我也谈到了这个解决方案。但是在这种情况下,您需要了解 for 循环中的每个子类,在我的情况下,如果我不必遍历每个子类类型并执行 if 语句会更好。但如果这是不可能的,那么我想我不走运!无论如何,谢谢。
    【解决方案3】:

    您应该使用子类的构造函数来克隆它。 否则它将是不能转换为 C 或 B 的超类 A 的类型 参见例如反思:http://tutorials.jenkov.com/java-reflection/constructors.html

    或者更好地使用克隆功能。 https://en.m.wikipedia.org/wiki/Clone_(Java_method)

    这应该容易得多。

    【讨论】:

    • 或者使用克隆功能。我猜这可能是最简单的。
    • 所以你是说在不知道你要实例化什么子类的情况下创建子类的实例是不可能的?当然我知道使用 .getClass() 是什么子类,但我不能用它来创建一个新实例?我也试过 .getClass().newInstance() 但这似乎没有我期望的行为。
    • 另外我要克隆的对象没有克隆方法。
    • 在这种情况下,如果您可以为对象编写克隆函数。我认为每个 java 对象都有一个克隆函数,因为它应该从 java.Object 扩展而来。但您可能需要覆盖克隆功能(请参阅javarevisited.blogspot.com/2015/01/…)。
    • 反射也应该是可能的,但这会更复杂 - 我认为。
    猜你喜欢
    • 2020-03-03
    • 2015-12-19
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 1970-01-01
    • 1970-01-01
    • 2013-01-12
    • 1970-01-01
    相关资源
    最近更新 更多