如何来回转换相同抽象 class 的两个 classes

How to cast two classes of the same abstract class back and forth

public abstract class Animal {

}

public class Dog implements Animal{

}

public class Cat implements Animal{

}

public static void main(String[] args){
    Dog dog = new Dog();
    Cat cat = new Cat();
}

如果我有上述结构,是否有办法将 Cat 的实例转换为 Dog,反之亦然?

没有。您误解了 cast 运算符。它用于三个 完全不相关的 任务。您可能知道 5 + 2 是 7,但 "hey" + "bye""heybye"+ 运算符用于 2 个 完全不相关的 任务。 cast 运算符也不例外。

因此,最好为 (SomeType) someExpr 的实际句法结构保留名称 'cast',即很少使用该术语,而是将名称用于您可以做的 3 件完全不相关的事情有了它:

原始转换

当 parens 中的东西是原始数据时,你会得到这个 'mode',具体来说,是数字原始数据:charintlongdoublefloatshortbyte。这就是整个列表——这些类型在 java 中被硬编码,你不能创建自己的基元,永远不会有与此不同的基元类型 1.

在此模式下,实际发生转换:

double y = 5.5;
int x = (int) y;

上面的转换这是转换运算符的唯一用法

类型断言

当您在括号中输入 non-primitive 时会出现此模式,并且仅适用于具体化部分(non-generics 部分,因此 <> 部分中的任何内容均无效类型)。

这是什么意思,如果一切顺利,没有。它根本不做任何事情。 无法转换任何东西

具体来说,它会注入如下代码:

  1. 检查表达式是否实际上是括号中类型的实例。

2a。如果是,那么什么也不做,但是为了解析代码并知道要做什么,表达式 (String) whatever 是字符串类型,即使 whatever 不是。

2b。如果不是,那就当场扔一个ClassCastException

这就是它所能做的。所以,如果它没有抛出异常,它什么都不做。

类型强制/泛型巫毒魔法

为了完整起见,我将这个包括在内:你不应该使用这种特定的施法模式,直到你在 java 和 none 中更先进在您对 java 了解相当多之前,它甚至是有意义的。如果这对您来说听起来像是官话,那很好。无视就好,不重要

对于 non-reified 括号中的内容(<> 中的内容),它确实确实 完全没有作用 。它不可能抛出任何东西,它实际上归结为零字节码。它只是告诉 javac 停止抱怨。是你告诉编译器:是的,我知道,你不能保证这些代码中的任何一个都有意义。闭嘴,我知道我在做什么

示例:

List<String> listOfStrings = new ArrayList<String>();
listOfStrings.add("Test");
List raw = listOfStrings;
List<Integer> numbers = (List<Integer>) raw;

以上编译(但有警告,因为这是毫无意义的疯狂代码),并运行,甚至没有抛出异常。即使您现在有一个 List<Integer> 类型的变量,其中包含一个 not-integer。如果您与此列表交互,ClassCastExceptions 将开始出现,即使您甚至没有编写任何转换。

狗和猫

您正在执行第二种模式:类型断言。 Dog 的实例不是 Cat 的实例,因此:

Dog dog = new Dog();
Cat c = (Cat) dog;

会失败。它实际上会因 compile-time 错误而失败,因为编译器知道它永远不会工作。您可以'fix':

Animal a = new Dog();
Cat c = (Cat) a;

现在可以编译了,但是会在第二行抛出 ClassCastException。

Animal a = new Dog() 是什么意思?

java 中的

Non-primitives 是 always 引用。在 java 中不可能有一个包含狗的变量。我知道 Dog dog = new Dog() 确实看起来你有一个名为 dog 的变量,它的值是一只狗,但是,它不是。它是 对狗的引用 。狗太奇怪了,太大了,变量是非常简单的东西。所有变量都像便利贴:它们有一个固定的、少量的 space。对于 intboolean 等,数字刚好在便利贴上,但对于所有 non-primitive,您不会将狗字面意思放在便条上。你在纸条上写狗的位置Dog dog = new Dog() 只是语法糖:

Dog dog;   // make a new postit note
new Dog(); // make a dog object
dog = what we just made; // scribble the dog's location on the note

现在 Animal a = new Dog() 说得通了:Animal a;Dog dog 都只是便利贴。只是这里的a便利贴是有限制的:它可以是空白的(a = null),也可以是动物的位置。你可以在上面写下猫、狗、驴或独角兽的位置。但是你不能在上面写房子的位置。 dog note 只能写狗的位置(或者留空)

因此,狗的位置可以写在任何一张纸条上。

Cat c = (Cat) animal;

表示:

  • 新建一张便利贴,命名为c,限制为只能写猫的位置,否则为空白。
  • 记下名为animal 的便利贴。跟随它并检查它带你去的动物是否真的是一只猫。如果不是,则抛出一个ClassCastException.
  • 但如果是,请记下那只猫的位置并将其写在标题为 'c' 的帖子上。

这是明智之举。

[1] 出于原语转换的目的,被称为 Project Valhalla 的未来语言更新不太可能改变这一点,尽管它或多或少会引入让您编写自己的原语的概念。