“嗨,阿米戈!今天课程的主题是拓宽和窄化类型转换。你很久以前就学了拓宽和窄化原始类型。第 10 级。今天我们要讲讲它如何用于引用类型,也就是类的实例。”

实际上,这一块相当简单。想象下类的继承链:类、父类、父类的父类等,一直回溯到 Object 类。由于一个类包含它所继承类的所有成员方法,因此这个类的实例可以保存在其类型是任何一个父类类型的变量中

下面是一个示例:

代码 说明
class Animal
{
public void doAnimalActions();
}class Cat extends Animal
{
public void doCatActions();
}class Tiger extends Cat
{
public void doTigerActions();
}
这里有三个类声明:Animal、Cat 和 Tiger。Cat 继承 Animal。Tiger 继承 Cat。
public static void main(String[] args)
{
Tiger tiger = new Tiger();
Cat cat = new Tiger();
Animal animal = new Tiger();
Object obj = new Tiger();
}
Tiger 对象可始终赋给类型是其父类类型的变量。对于 Tiger 类,分别是 Cat、Animal 和 Object。

现在我们来看一下拓宽和窄化转换。

如果赋值操作使我们在继承链中向上移动(靠近 Object 类),那么我们正在处理拓宽转换(也称为向上转换)。如果我们沿着对象类型的链向下移动,那么这就是窄化转换(也称为向下转换)。

沿继承链上移称为拓宽,因为它趋向更为普遍的类型。不过,这样我们就无法调用通过继承添加到该类的方法。

代码 说明
public static void main(String[] args)
{
Object obj = new Tiger();
Animal animal = (Animal) obj;
Cat cat = (Cat) obj;
Tiger tiger = (Tiger) animal;
Tiger tiger2 = (Tiger) cat;
}
窄化类型时,你要使用类型转换运算符,即我们执行显式转换。

这就造成 Java 机器检查对象是否确实继承我们要转换到的类型。

这个小小的创新极大减少了类型转换错误数量,并且显著提高了 Java 程序的稳定性。

代码 说明
public static void main(String[] args)
{
Object obj = new Tiger();
if (obj instanceof Cat)
{
Cat cat = (Cat) obj;
cat.doCatActions();
}}
更重要的是,使用 instanceof 检查
public static void main(String[] args)
{
Animal animal = new Tiger();
doAllAction(animal);

Animal animal2 = new Cat();
doAllAction(animal2);

Animal animal3 = new Animal();
doAllAction(animal3);
}

public static void doAllAction(Animal animal)
{
if (animal instanceof Tiger)
{
Tiger tiger = (Tiger) animal;
tiger.doTigerActions();
}

if (animal instanceof Cat)
{
Cat cat = (Cat) animal;
cat.doCatActions();
}

animal.doAnimalActions();
}
这就是原因所在。看看左侧的示例。

我们(我们的代码)并不总是了解我们使用的对象类型。它可能是与变量 (Animal) 相同类型的对象,也可能是后代类型 (Cat,Tiger) 的对象。

不妨考虑 doAllAction 方法。无论所传递的对象类型如何,它都可以正常运行。

换句话说,它可正确处理全部三种类型:Animal、Cat 和 Tiger。

public static void main(String[] args)
{
Cat cat = new Tiger();
Animal animal = cat;
Object obj = cat;
}
这里有三个赋值运算。这些都是拓宽转换的例子。

这里不需要类型转换运算符,因为根本不需要检查。对象引用始终存储在其类型是祖先类型的变量中。

“哦,倒数第二个例子清楚地说明了一切:为何需要检查,以及为何需要类型转换。”

“我希望如此。我要提醒你注意这一点:“

这些都不会导致对象发生任何变化!唯一变化的是特定引用变量上可调用的方法数量

例如,Cat 变量可让你调用 doAnimalActions 和 doCatActions 方法。即使它指向 Tiger 对象,它也对 doTigerActions 方法一无所知。

“好,我明白了。这比我想象的要容易。”