了解Java中的多重强制转换

了解Java中的多重强制转换,java,inheritance,casting,Java,Inheritance,Casting,如果你想马上读问题,请跳到最后一句 假设我们有一个接口和三个类: interface I{} class A implements I{} class B extends A {} 以及下列声明: A a = new A(); B b = new B(); 现在,有一种经典的强制转换方法,允许我将类型a(父类)的引用强制转换为类型B(子类)的对象,如下所示: a = b; //here a is no longer pointing at object A, and is now po

如果你想马上读问题,请跳到最后一句

假设我们有一个接口和三个类:

interface I{}

class A implements I{}

class B extends A {}
以及下列声明:

A a = new A();
B b = new B();
现在,有一种经典的强制转换方法,允许我将类型a(父类)的引用强制转换为类型B(子类)的对象,如下所示:

 a = b; //here a is no longer pointing at object A, and is now pointing at the same object b is pointing at.
 b = (B) a; // the casting is now accepted by the compiler and during runtime as well.
a =
  // outer CastExpression
  (B)(
    // with its UnaryExpression being another CastExpression
    (I)(b)
  );
但问题出在这里。每次我看到一行带有多次转换的代码时,我都无法(逐字地)阅读它,因此,我无法理解它的含义

例如,假设我们有一行代码:

a = (B)(I)b;
a = (B)(I)b;
你怎么看这个<代码>a是对类型a对象的引用,它被赋予类型B对象的值(从左侧开始第一次转换)。但是等一下,b前面还有另一个演员。我们这里有什么?它是一个被转换为(B)对象的接口吗?或者它是一个b被转换为一个接口,而这个接口也被转换为a(b)

为了避免混淆,我尝试将其分解:

I temp = (I) b;// first line
a = (B) temp;// second line
因此,首先,由于b是一个I(因为它扩展了实现I的A),“第一行”被编译器和运行时接受

“第二行”虽然,我们有一个对对象a的引用,它被赋予类型B的值。乍一看,它没有什么问题。但后来我意识到
I
不是A,也不是B,即使“第二行”中的强制转换可以使编译器相信它是B类型的对象,但在运行时它不应该被接受

因此,我想回答的主要问题是,我如何理解下面这句话:

b
转换为
I
,然后将其转换为
b
。大概是因为您不能直接将
b
转换为
b

现在并没有很好的情况可以使用它,因为如果可能的话,即使是一次施法也应该避免。但是如果你想要

String s = (String) myHashMap;
要编译,您需要向上转换以防止编译器禁止明显非法的转换:

String s = (String) (Object) myHashMap;
当然,如果您的
myHashMap
不为null,这将导致运行时
ClassCastException
,但与前面的示例不同,它将编译。

基于

a = (B)(I)b;
可以更好地可视化如下:

 a = b; //here a is no longer pointing at object A, and is now pointing at the same object b is pointing at.
 b = (B) a; // the casting is now accepted by the compiler and during runtime as well.
a =
  // outer CastExpression
  (B)(
    // with its UnaryExpression being another CastExpression
    (I)(b)
  );
也就是说,它将
b
强制转换为
I
,然后将其强制转换为
b
,然后将其分配给
A
变量


然而,这两种类型看起来都不是必需的。如果
b
b
的一个实例,那么它也是
A
I
现实或你不想要的答案的一个实例

这里真正的问题是一个粗心的傻瓜写了蹩脚的代码。 真正的解决办法是;要么不要写蹩脚的代码,要么修复代码

让我们继续做一个傻瓜或是你想要的答案

java中有两种类型的强制转换;上浇铸和下浇铸

上转换是将类型A的对象转换为接口I的实例;您正在“向上”转换继承树。 例如:

A aObject = new A();
I iObject = (I)aObject;  // Up casting.
A aObject;
I iObject = new A();

aObject = (A)iObject;
向上转换的好处是编译器能够在编译时确定转换是否合法

向下投射是指将类型I的对象投射为类型A的对象;您正在“打倒”继承树。 例如:

A aObject = new A();
I iObject = (I)aObject;  // Up casting.
A aObject;
I iObject = new A();

aObject = (A)iObject;
编译器在编译时不知道向下转换是否会成功。 因此,向下转换可能在运行时引发异常

你令人困惑的代码:
a=(B)(I)B是向上投射(安全)和向下投射(不安全)的示例

作为一个额外的奖励,演员阵容是不需要的。 将B对象直接指定给a引用总是安全的,因为B类扩展了a类

称呼:“粗心的傻瓜”似乎是一种很强的语言。 这不是一种强有力的语言,它是描述你的处境的最好的方式。
事实上,像这样编写代码的人应该被终止(或者,让你的竞争对手雇用他们)。

也许你没有意识到,即使强制转换到I或A,b仍然是类型b的对象,它也不会失去它的本质。考虑铸造一种对java说“将我的对象视为I类型的对象”的方式。如果是B型的话,它也是A型的

因此,该指令告诉java‘使用我的对象,因为它是类型I,使用后立即作为类型B,通过声明,它也是类型A。因此没有编译或运行时错误


然后我们可以看到。它看起来也大多是无用和丑陋的。

我会把它解释为两个不必要的强制转换。向接口强制转换似乎是错误的。
I
不是
B
,但是,
I
可能是
B
:)OP的实际困惑,我想,是他认为强制转换会改变对象的运行时类型。否。对象的运行时类型是固定的。但在编译时,编译器可以将其视为不同的类型(应该是特定运行时类型的超级类型)。值得一提的是,您应该(几乎)永远不需要强制转换。写得好的代码99%没有强制转换,但问题是{a=(B)(I)B;}在运行时也被接受。b是a I,所以这个类型没有问题,但是为什么可以将I转换为a b呢?如果我没有弄错的话,我不是B。双重强制转换的用例:@ZhongYu正确,不过最好通过修改实际的泛型类型和更宽的范围来解决。编译器总是允许缩小转换范围。这是它不好的原因之一。运行时出现的错误造成了巨大的伤害。这就是为什么这里有这么多的受访者反对收窄转换。Java官方术语“向上转换”是“扩大转换”,而“向下转换”是“收窄转换”。使用官方术语,可以直接在JLS中查找规则。“cast”的过去英语分词是“cast”。不用担心,@Massimo,你的英语几乎和我见过的任何人一样好