昨天我刚开始学习接口,一直在做一些简单的例子,我注意到我在理解类和接口之间的转换时遇到了很多麻烦,所以我阅读了Java Cast Interface to Class以及Interfaces Oracle Java Tutorials
但是当我的书在底部给出了一个小小的评论问题时,我发现我仍然没有完全理解,而且我买的书没有解决方案。以下是问题(我给出了我的尝试和推理,这对某些人来说可能是错误的,所以任何帮助都是好的!)
假设 Sandwich 类实现了 Edible 接口,并且你得到了变量声明
Sandwich sub = new Sandwich();
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
Edible e = null;
Run Code Online (Sandbox Code Playgroud)
以下哪些赋值语句是合法的?
e = sub;
由于 Sandwich 类实现了 interface Edible,所以这没有问题。我们可以说e = sub没有问题。有用
sub = e;
由于我们试图sub将Sandwitch类中的对象更改为interface类型,因此不进行强制转换就无法做到这一点。它不会工作
sub = (Sandwich) e
有效!这解决了我们的老问题
sub = (Sandwich) cerealBox;
我不知道..但它应该工作吗? cerealBox是Rectangle这样,(Sandwich)我们将其转换为sub,这是Sandwich
e =cerealBox;
不要这么认为。Rectangle没有实现Edible接口所以它不应该工作
e = (Edible) cerealBox;
现在应该工作。(Edible) 就好像它实现了接口一样。
e = (Rectangle) cerealBox;
不确定。我认为这行不通,我的意思cerealBox是 Rectangle 类型,我们为什么要rectangle再次制作它?
e = (Rectangle) null;
完全不确定
您需要了解的是,每个对象都有一个具体的类。它“是”该类的一个实例,并且“是”该类继承的每个类的实例,所有这些都是同时的。不仅如此,它“是”这些类中的任何一个继承的每个接口的实例,所有这些都是同时的。
所以,假设:
class Animal
class Mammal extends Animal implements Suckler
class Dog extends Mammal implements Woofer
Run Code Online (Sandbox Code Playgroud)
...如果我创建 anew Dog()那么该对象“是” an Object(因为所有对象都继承Object)、 an Animal、 a Mammal、 a Suckler、 aDog和 a Woofer,都在同一时间。
但是,变量与对象不同。一个变量指向一个对象,一个变量有一个类型。类型必须与分配的对象兼容,仅此而已。
所以:
Suckler s = new Dog();
Run Code Online (Sandbox Code Playgroud)
工作,但从那一刻起,编译器通过s变量知道的关于对象的所有内容是它是一个Suckler. 它不知道这是一只狗;它不知道它是哺乳动物。所以我们不能去:
Dog d = s;
Run Code Online (Sandbox Code Playgroud)
...因为编译器不能保证指向的变量s是 a Dog。
变量的类型永远不能改变。无论如何,在其整个生命周期中s都有类型Suckler。我们可以将 aSheep或 a分配Pig给s,但除了作为定义一部分的操作之外,我们无法对这些对象执行任何操作Suckler。
我将假设各种类和接口的定义如下:
public interface Edible {
...
}
public class Sandwich implements Edible {
...
}
public class Rectangle { // note, does not implement or extend anything
// (except Object)
...
}
Run Code Online (Sandbox Code Playgroud)
所以:
Sandwich sub = new Sandwich();
Edible e = null;
e = sub;
Run Code Online (Sandbox Code Playgroud)
这可以。sub是一种Sandwich,并且Sandwich是一种Edible。
Sandwich sub = new Sandwich();
Edible e = null;
sub = e;
Run Code Online (Sandbox Code Playgroud)
这不会编译。e是Edible,但可以有任意数量的类实现Edible,以及Sandwich。sub必须是 aSandwich并且由于编译器不能确定这e是一个三明治,它会拒绝编译。
Sandwich sub = new Sandwich();
Edible e = null;
sub = (Sandwich) e;
Run Code Online (Sandbox Code Playgroud)
这有效。正如您正确计算出的那样,演员表告诉编译器“好吧,您不能确定那e是一个Sandwich,但作为编码器,我告诉您它是。
如果你这样做了,并且在运行时e实际上是一个Apple implements Edible,而不是一个Sandwich,JRE 会抛出一个ClassCastException. 你的工作是确保这不会发生——避免强制转换是最好的方法。
Sandwich sub = new Sandwich();
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
sub = (Sandwich) cerealBox;
Run Code Online (Sandbox Code Playgroud)
...将拒绝编译。Sandwich并且Rectangle彼此没有关系。编译器知道 noSandwich也是 a Rectangle,所以它拒绝编译。
变量sub必须始终指向 a Sandwich,并且cerealBox必须始终指向 a Rectangle。aRectangle可能是 a的唯一方法Sandwich是 ifRectangle继承Sandwich,反之亦然。由于这两种情况都不是,它不会编译。
这是假设上述声明。一个类可以实现多个接口,因此如果是public class Sandwich implements Edible, Rectangle {...},则此代码将起作用。
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
Edible e = null;
e = cerealBox;
Run Code Online (Sandbox Code Playgroud)
...不会编译。ARectangle不是Edible.
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
Edible e = null;
e = (Edible) cerealBox;
Run Code Online (Sandbox Code Playgroud)
..乍一看,你可能会认为不会编译。ARectangle不是Edible,你不能告诉编译器它是。但是编译器不能保证没有这样的类:
public class Flapjack extends Rectangle implements Edible { ... }
Run Code Online (Sandbox Code Playgroud)
AFlapjack将是一种Rectangle也是 an Edible,并且由于编译器不够聪明,无法知道它cerealBox不是 a Flapjack,因此它必须编译(它会在运行时失败)。
一个真正聪明的编译器可能能够分析程序逻辑,看看它cerealBox已被初始化为new Rectangle(),并且在运行时没有机会改变它。但是 Java 标准没有那种复杂的静态分析。
的作者Rectangle可以Square通过将其定义为public final class Rectangle--final关键字禁止子类来确保它不存在。
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
Edible e = null;
e = (Rectangle) cerealBox;
Run Code Online (Sandbox Code Playgroud)
...不会编译。e仍然是Edible. 您不能为其分配 a Rectangle,因为Edible和Rectangle不相关。
Edible e = null;
e = (Rectangle) null;
Run Code Online (Sandbox Code Playgroud)
将 null 强制转换为 aRectangle很好,但是ean 是Edible,并且您不能将 a 分配Rectangle给 an Edible,因为它们是不相关的类型。