Java 类不能向下转换为具有相同字段的直接子类

Java 类不能向下转换为具有相同字段的直接子类,java,clone,Java,Clone,我正在制作一个包含几个扑克牌类的包,它们之间的区别在于,它们在自然顺序方面有不同的自由DefaultCard具有“默认顺序”(ace high,2

我正在制作一个包含几个扑克牌类的包,它们之间的区别在于,它们在自然顺序方面有不同的自由
DefaultCard
具有“默认顺序”(ace high,2<3<4<…ConstantOrderingCard允许通过构造函数设置顺序,但实例化后不能更改<代码>VariableOrderingCard允许在实例化期间和之后设置排序

类层次结构如下所示:

              [AbstractCard]             [Joker]
             /            \
         [DefaultCard]   [ConstantOrderingCard]
                             \
                         [VariableOrderingCard]
为了更容易地复制数据组,我让所有这些类实现了
Cloneable
,并重写了它们的
clone
方法
AbstractCard
DefaultCard
Joker
没有可变的引用类型字段,因此我用同样非常简单的方式对它们进行了重写。这就是我所做的(我以AbstractCard为例):

由于
ConstantOrderingCard
确实有可变的引用类型字段,因此我返回了一个显式副本
VariableOrderingCard
没有未从
ConstantOrderingCard
继承的字段;它包含的都是setter。所以,我想我可以调用ConstantOrderingCard.clone,然后像上面一样向下播放它。每个类的
clone
方法,但是
VariableOrderingCard
编译没有错误,并且在运行时工作得很好,但是
VariableOrderingCard
给了我一个
ClassCastException
,说我不能将
ConstantOrderingCard
转换为
VariableOrderingCard
。我不知道为什么会这样,因为它没有定义自己的领域,尤其是因为它对所有其他领域都有效。对此有任何见解都将不胜感激

以下是
ConstantOrderingCard
clone
方法:

@Override
protected ConstantOrderingCard clone() throws CloneNotSupportedException
{
    if (rankOrdering != DEFAULT_RANK_ORDERING &&
        suitOrdering != DEFAULT_SUIT_ORDERING)
    {
        int[] rankOrderingCopy = rankOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        int[] suitOrderingCopy = suitOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        return new ConstantOrderingCard(RANK, SUIT, rankOrderingCopy,
            suitOrderingCopy, compareSuitsFirst);
    }
    else if (rankOrdering != DEFAULT_RANK_ORDERING)
    {
        int[] rankOrderingCopy = rankOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        return new ConstantOrderingCard(RANK, SUIT, rankOrderingCopy,
            compareSuitsFirst);
    }
    else if (suitOrdering != DEFAULT_SUIT_ORDERING)
    {
        int[] suitOrderingCopy = suitOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        return new ConstantOrderingCard(RANK, SUIT, suitOrderingCopy,
            compareSuitsFirst);
    }
    else
    {
        return new ConstantOrderingCard(RANK, SUIT, compareSuitsFirst);
    }
}
@Override
protected VariableOrderingCard clone() throws CloneNotSupportedException
{
    return (VariableOrderingCard)super.clone();
}
下面是
VariableOrderingCard
克隆方法:

@Override
protected ConstantOrderingCard clone() throws CloneNotSupportedException
{
    if (rankOrdering != DEFAULT_RANK_ORDERING &&
        suitOrdering != DEFAULT_SUIT_ORDERING)
    {
        int[] rankOrderingCopy = rankOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        int[] suitOrderingCopy = suitOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        return new ConstantOrderingCard(RANK, SUIT, rankOrderingCopy,
            suitOrderingCopy, compareSuitsFirst);
    }
    else if (rankOrdering != DEFAULT_RANK_ORDERING)
    {
        int[] rankOrderingCopy = rankOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        return new ConstantOrderingCard(RANK, SUIT, rankOrderingCopy,
            compareSuitsFirst);
    }
    else if (suitOrdering != DEFAULT_SUIT_ORDERING)
    {
        int[] suitOrderingCopy = suitOrdering.values().stream().mapToInt(
            Integer::intValue).toArray();
        return new ConstantOrderingCard(RANK, SUIT, suitOrderingCopy,
            compareSuitsFirst);
    }
    else
    {
        return new ConstantOrderingCard(RANK, SUIT, compareSuitsFirst);
    }
}
@Override
protected VariableOrderingCard clone() throws CloneNotSupportedException
{
    return (VariableOrderingCard)super.clone();
}

Object.clone始终创建与被克隆对象相同的类的实例,但您在ConstantOrderingCard中实现的clone()未使用Object.clone,因此不会出现Java系统级的“魔术”
new ConstantOrderingCard
始终创建ConstantOrderingCard的实例,而不创建其他实例

另一种方法是继续使用Object.clone,然后让您的自定义
clone
方法考虑需要深度复制的字段:

@Override
protected ConstantOrderingCard clone() throws CloneNotSupportedException
{
    ConstantOrderingCard copy = (ConstantOrderingCard) super.clone();

    copy.rankOrdering = new HashMap<>(copy.rankOrdering);
    copy.suitOrdering = new HashMap<>(copy.suitOrdering);

    return copy;
}
@覆盖
受保护的ConstantOrderingCard克隆()引发CloneNotSupportedException
{
ConstantOrderingCard copy=(ConstantOrderingCard)super.clone();
copy.rankOrdering=新哈希映射(copy.rankOrdering);
copy.suiteordering=新HashMap(copy.suiteordering);
返回副本;
}

(您还没有显示ConstantOrderingCard类的其余部分,因此我不确定这是否足够,但您已经明白了。)

Object.clone始终创建与被克隆对象相同的类的实例,但ConstantOrderingCard中clone()的实现没有使用Object.clone,因此没有Java系统级别“魔法”正在发生。
新建ConstantOrderingCard
始终创建ConstantOrderingCard的实例,而不创建其他实例

另一种方法是继续使用Object.clone,然后让您的自定义
clone
方法考虑需要深度复制的字段:

@Override
protected ConstantOrderingCard clone() throws CloneNotSupportedException
{
    ConstantOrderingCard copy = (ConstantOrderingCard) super.clone();

    copy.rankOrdering = new HashMap<>(copy.rankOrdering);
    copy.suitOrdering = new HashMap<>(copy.suitOrdering);

    return copy;
}
@覆盖
受保护的ConstantOrderingCard克隆()引发CloneNotSupportedException
{
ConstantOrderingCard copy=(ConstantOrderingCard)super.clone();
copy.rankOrdering=新哈希映射(copy.rankOrdering);
copy.suiteordering=新HashMap(copy.suiteordering);
返回副本;
}

(您还没有显示ConstantOrderingCard类的其余部分,所以我不确定这是否足够,但您已经明白了。)

在继承层次结构中,VariableOrderingCard是ConstantOrderingCard,但这并不意味着ConstantOrderingCard是VariableOrderingCard

调用ConstantOrderingCard.clone()时,将创建ConstantOrderingCard类型的对象。如果子类型的对象具有超类型的引用,则可以进行向下转换。不幸的是,在这种情况下,您具有超类型的对象

假设您创建了一个字符串并将其存储为对象引用:

//will compile
Object o = new String("hello world");
String s = (String) o;
但是,VariableOrderingCard引用只能用于VariableOrderingCard对象

//will also compile
ConstantOrderingCard c = new VariableOrderingCard(...);
VariableOrderingCard v = (VariableOrderingCard) c;
但是,调用super.clone()会创建ConstantOrderingCard类型的对象,相当于:

//not gonna compile 
VariableOrderingCard v = (VariableOrderingCard) new ConstantOrderingCard(...);
有关此主题的完整文章:

在继承层次结构中,VariableOrderingCard是一个ConstantOrderingCard,但这并不意味着ConstantOrderingCard是一个VariableOrderingCard

调用ConstantOrderingCard.clone()时,将创建ConstantOrderingCard类型的对象。如果子类型的对象具有超类型的引用,则可以进行向下转换。不幸的是,在这种情况下,您具有超类型的对象

假设您创建了一个字符串并将其存储为对象引用:

//will compile
Object o = new String("hello world");
String s = (String) o;
但是,VariableOrderingCard引用只能用于VariableOrderingCard对象

//will also compile
ConstantOrderingCard c = new VariableOrderingCard(...);
VariableOrderingCard v = (VariableOrderingCard) c;
但是,调用super.clone()会创建ConstantOrderingCard类型的对象,相当于:

//not gonna compile 
VariableOrderingCard v = (VariableOrderingCard) new ConstantOrderingCard(...);
有关此主题的完整文章:
super.clone()
返回使用
新ConstantOrderingCard(…)
创建的
ConstantOrderingCard
,因此在运行时它不是
VariableOrderingCard
,无法强制转换,错误是正常的

使用Java8,您可以这样做来动态创建所需类型的对象(我复制了一个类似的层次结构,只保留了基本的部分,为重要的类提供了虚拟字段,您会明白的