将对象转换为未实现的接口

时间:2015-02-11 00:13:30

标签: java interface casting implementation abstract

当该对象实际上没有实现接口时,是否可以将对象强制转换为接口?例如:

矩形类未实现Edible -

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

这是有效的吗?

3 个答案:

答案 0 :(得分:2)

这将编译,但您将在运行时获得ClassCastException,因为Rectangle没有实现Edible

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

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

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

请注意,如果您使Edible成为一个类,即使是abstract,那么编译器也会在强制转换上生成错误,因为它已确定Rectangle不能是Edible 1}},因为Rectangle没有子类EdibleRectangle的子类也不能是Edible,因为Java不允许扩展多个类。

另请注意,如果您创建Rectangle final,则编译器将在强制类型转换器上生成错误,因为Rectangle没有子类,例如EdibleRectangle上面可能会实现Edible接口。

答案 1 :(得分:0)

没有。由于未实现接口,因此您将获得ClassCastException。

答案 2 :(得分:0)

在这种情况下,如果您编写此编译器,编译器将不会抱怨,但在运行时,您将获得未经检查的运行时异常:特别是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

因此应允许此演员表(参见下面的示例)。程序员应该使用ClassCastException来防范instanceof

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
    
  3. 在这两种情况下,无论代码是否有任何可能的扩展,编译器都可以判断转换是非法的。因此它阻止你这样做。

    参考文献: