【问题标题】:"Dynamic" Casting in JavaJava 中的“动态”转换
【发布时间】:2008-12-30 20:31:39
【问题描述】:

大家好,

想知道是否有任何 Java 黑客可以提示我为什么以下方法不起作用:

public class Parent {
    public Parent copy() {
       Parent aCopy = new Parent();
       ...
       return aCopy;
    }
}

public class ChildN extends Parent {
    ...
}

public class Driver {
     public static void main(String[] args) {
         ChildN orig = new ChildN();
         ...
         ChildN copy = orig.getClass().cast(orig.copy());
     }
}

代码编译起来很开心,但决定在运行时抛出 ClassCastException D=

编辑:哇,回复真快。多谢你们!所以看来我不能使用这种方法进行向下转换......还有其他方法可以在 Java 中进行向下转换吗?我确实考虑过让每个 ChildN 类覆盖 copy(),但并不热衷于添加额外的样板代码。

【问题讨论】:

  • 你可以做到。看看我的编辑。我认为您首先在理解“铸造”时遇到了问题。

标签: java inheritance casting


【解决方案1】:

就像尝试这样做:

  public Object copy(){
       return new Object();
  }

然后尝试:

  String s = ( String ) copy();

您的Parent 类和ChildN 类与ObjectString

的关系相同

要使其正常工作,您需要执行以下操作:

public class ChildN extends Parent {
    public Parent copy() {
        return new ChildN();
    }
}

也就是说,重写“复制”方法并返回正确的实例。


编辑

根据您的编辑。这实际上是可能的。这可能是一种可能的方式:

public class Parent {
    public Parent copy() {
        Parent copy = this.getClass().newInstance();
        //...
        return copy;
    }
}

这样您就不必在每个子类中重写“复制”方法。这就是原型设计模式。

但是使用这个实现你应该知道两个检查异常。这是编译和运行没有问题的完整程序。

public class Parent {
    public Parent copy()  throws InstantiationException, IllegalAccessException  {
       Parent copy = this.getClass().newInstance();
       //...
       return copy;
    }
}
class ChildN  extends Parent {}

class Driver {
     public static void main(String[] args) throws  InstantiationException ,  IllegalAccessException  {
         ChildN orig = new ChildN();
         ChildN copy = orig.getClass().cast(orig.copy());
         System.out.println( "Greetings from : " + copy );
    }
}

【讨论】:

  • 以身作则!这个答案对我来说非常很清楚。现在还可以声明 Child.copy 以返回 ChildN iso 父级吗? (我知道在 C++ 中可以)
  • 不是Java。至少不是在 Java 源代码中。为了稍微混淆这个问题,它 is 在 Java 字节码中是允许的...
【解决方案2】:

演员们正在有效地尝试这样做:

ChildN copy = (ChildN) orig.copy();

(它正在计算在执行时执行的强制转换,但这就是因为orig.getClass() 将是ChildN.class)但是,orig.copy() 不返回 ChildN 的实例,它返回的实例只是Parent,所以它不能转换为ChildN

【讨论】:

    【解决方案3】:

    如果 ChildN 没有覆盖 copy() 以返回 ChildN 的实例,那么您正在尝试将 parent 类型的对象向下转换为 ChildN 类型

    【讨论】:

      【解决方案4】:

      如果 Class#isInstance() 返回 false,java.lang.Class#cast(Object) 会抛出 ClassCastException。来自该方法的 javadoc:

      判断指定的对象是否为 与对象赋值兼容 由这个类代表。这种方法 是 Java 的动态等价物 语言实例运算符... 具体来说,如果这 Class 对象代表一个 声明类,此方法返回 true 如果指定 Object 参数是 表示类的实例(或 其任何子类);它返回 false 否则。

      由于 Parent 不是 child 的子类,isInstance() 返回 false,因此 cast() 抛出异常。这可能违反了最小惊讶原则,但它的工作方式与记录的方式相同 - cast() 只能向上转换,不能向下转换。

      【讨论】:

        【解决方案5】:

        您可能只是想要一个对象的副本/克隆。

        在这种情况下,实现 Cloneable 接口并根据需要覆盖 clone()。

        public class Parent implement Cloneable {
           public Object clone() throws CloneNotSupportedException {
             Parent aCopy = (Parent) super.clone();
           ...
           return aCopy;
           }
        }
        
        public class ChildN extends Parent {
            ...
        }
        
        public class Driver {
             public static void main(String[] args) {
                 ChildN orig = new ChildN();
                 ...
                 ChildN copy = orig.getClass().cast(orig.clone());
             }
        }
        

        这正是您的“copy()”方法试图以 Java 方式执行的操作。

        (是的,你也可以做花哨的内省游戏,但是一旦你没有公共的默认构造函数,这些方法就会失败或变得丑陋。无论你有什么构造函数,克隆在任何情况下都可以工作。)

        【讨论】:

        • 也有效!我听说 clone() 名声不好,但不确定具体原因。
        • 这可能是“其他”可能的方式。无需更改方法签名(使用克隆和返回对象)复制作为名称和父作为返回应该这样做。
        【解决方案6】:

        (无法在评论中添加代码,所以我会在这里添加)

        关于 Cloneable:如果你正在实现 Cloneable,请按如下方式实现;打电话更干净...

        public class Foo implements Cloneable {
            public Foo clone() {
                try {
                    return (Foo) super.clone();
                } catch (CloneNotSupportedException e) {
                    return null; // can never happen!
            }
        }
        

        [编辑:我也看到其他人使用

        throw new AssertionError("This should never happen! I'm Cloneable!");
        

        在 catch 块中。]

        【讨论】:

        • 确实,我就是这样做的!虽然我的评论更像是 // JAVA EPIC FAIL :)
        【解决方案7】:

        向下转换不起作用的原因是,当您将父对象转换为子类型时,您无法在父对象上调用子类型的方法。但它的工作方式是相反的......

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-07
          • 1970-01-01
          • 1970-01-01
          • 2012-01-18
          • 1970-01-01
          相关资源
          最近更新 更多