接口和类之间的转换

Mun*_*lam 3 java interface

昨天我刚开始学习接口,一直在做一些简单的例子,我注意到我在理解类和接口之间的转换时遇到了很多麻烦,所以我阅读了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)

以下哪些赋值语句是合法的?

  1. e = sub;

    由于 Sandwich 类实现了 interface Edible,所以这没有问题。我们可以说e = sub没有问题。有用

  2. sub = e;

    由于我们试图subSandwitch类中的对象更改为interface类型,因此不进行强制转换就无法做到这一点。它不会工作

  3. sub = (Sandwich) e

    有效!这解决了我们的老问题

  4. sub = (Sandwich) cerealBox;

    我不知道..但它应该工作吗? cerealBoxRectangle这样,(Sandwich)我们将其转换为sub,这是Sandwich

  5. e =cerealBox;

    不要这么认为。Rectangle没有实现Edible接口所以它不应该工作

  6. e = (Edible) cerealBox;

    现在应该工作。(Edible) 就好像它实现了接口一样。

  7. e = (Rectangle) cerealBox;

    不确定。我认为这行不通,我的意思cerealBox是 Rectangle 类型,我们为什么要rectangle再次制作它?

  8. e = (Rectangle) null;

    完全不确定

sli*_*lim 6

您需要了解的是,每个对象都有一个具体的类。它“是”该类的一个实例,并且“是”该类继承的每个类的实例,所有这些都是同时的。不仅如此,它“是”这些类中的任何一个继承的每个接口的实例,所有这些都是同时的

所以,假设:

 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分配Pigs,但除了作为定义一部分的操作之外,我们无法对这些对象执行任何操作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)

这不会编译。eEdible,但可以有任意数量的类实现Edible,以及Sandwichsub必须是 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,因为EdibleRectangle不相关。


Edible e = null;
e = (Rectangle) null;
Run Code Online (Sandbox Code Playgroud)

将 null 强制转换为 aRectangle很好,但是ean 是Edible,并且您不能将 a 分配Rectangle给 an Edible,因为它们是不相关的类型。