【问题标题】:Casting an object to an unimplemented interface将对象强制转换为未实现的接口
【发布时间】:2015-04-11 05:33:55
【问题描述】:

当一个对象实际上并没有实现接口时,你能把一个对象转换成一个接口吗?例如:

矩形类没有实现 Edible-

Rectangle cerealBox = new Rectangle();
Edible e;
e = (Edible) cerealBox;

这有效吗?

【问题讨论】:

  • 在这里提问并可能永远等待一个可能错误的答案或一组令人困惑的答案真的比直接尝试并立即得到正确答案更有效吗?

标签: java interface casting implementation abstract


【解决方案1】:

这会编译,但你会在运行时得到一个ClassCastException,因为Rectangle 没有实现Edible

为什么这甚至可以编译?毕竟,编译器可以确定Rectangle 没有实现Edible。因为引用变量cerealBox 可以在任何时候引用Rectangle 的某个未知子类,它确实实现了Edible

public class EdibleRectangle extends Rectangle implements Edible {
   //...
}

因此编译器必须允许强制转换,但会在运行时检查类型,如果不是Edible,则生成ClassCastException

请注意,如果您将Edible 设为一个类,甚至是abstract,那么编译器将在强制转换时产生错误,因为它已确定Rectangle 不能是Edible,因为Rectangle不继承 EdibleRectangle 的子类也不能是 Edible,因为 Java 不允许您扩展多个类。

还请注意,如果您将Rectangle final 设为final,那么编译器将在强制转换时产生错误,因为可能没有Rectangle 的子类(例如上面的EdibleRectangle)可能实现Edible界面。

【讨论】:

    【解决方案2】:

    没有。由于接口没有实现,你会得到一个 ClassCastException。

    【讨论】:

      【解决方案3】:

      在这种情况下,如果您编写此代码,编译器不会报错,但在运行时您会得到一个未经检查的运行时异常:特别是 ClassCastException。未经检查的运行时异常通常表示由于完全可预防的错误代码而导致的异常 - 在这种情况下,本可以阻止的非法强制转换。

      如何防止这个异常?

      如果您必须将一种变量类型转换为另一种类型,那么为了防止出现这种异常,您应该首先使用类型比较运算符instanceof 进行检查。例如:

      if (cerealBox instanceof Edible) {
        Edible e = (Edible) cerealBox;
        // munch away
      }
      else {
        // spit it out - it's not edible (or there's nothing there)
      }
      

      只有当变量是被测试的类型(或该类型的子类型)或者它实现了被测试的接口时,这个检查才会返回true。如果变量是null,它将返回false

      为什么编译器会让你做危险的事情?

      如果要转换的类型未标记为 final 并且转换是针对接口的,编译器不会为潜在的非法转换给出错误。这是因为:

      • 代码是可扩展的,因此未来的开发人员可以随时添加Rectangle 的新子类型,最终可能会分配给变量cerealBox:该变量是多态的;和
      • Rectangle 的任何此类子类型都可以实现 Edible

      因此应该允许这种转换(参见下面的示例)。程序员可以使用instanceof 来防范ClassCastException

      public class Soggy extends Rectangle implements Edible {}
      ...
      // cerealBox is actually Soggy: it is polymorphic
      Rectangle cerealBox = new Soggy(); 
      if (cerealBox instanceof Edible)
        Edible e = (Edible) cerealBox;
      

      如果出现以下情况,编译器将给出强制转换错误:

      1. 源类型被标记为final,并且它或其超类型都没有实现被转换为的接口;

        public class Card {} // not edible
        public final class Rectangle extends Card {}  // final, not edible
        ...
        Rectangle cerealBox = new Rectangle();
        Edible e = (Edible) cerealBox; // won't compile
        
      2. 强制转换为一个类,而源类型不是该类的子类型。

        public class Circle {}
        // neither this nor its subtypes will ever be Circle
        public class Rectangle {} 
        ...
        Rectangle cerealBox = new Rectangle();
        Circle e = (Circle) cerealBox; // won't compile
        

      在这两种情况下,无论您的代码是否有任何潜在扩展,编译器都可以判断出强制转换是非法的。因此它会阻止你这样做。

      参考文献:

      【讨论】:

        猜你喜欢
        • 2019-07-31
        • 2014-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多