Java 中的显式类型转换示例

2022-09-01 07:17:46

我在 http://www.javabeginner.com/learn-java/java-object-typecasting 上遇到过这个例子,在它谈论显式类型转换的部分,有一个例子让我感到困惑。

示例:

class Vehicle {

    String name;
    Vehicle() {
        name = "Vehicle";
    }
}

class HeavyVehicle extends Vehicle {

    HeavyVehicle() {
        name = "HeavyVehicle";
    }
}

class Truck extends HeavyVehicle {

    Truck() {
        name = "Truck";
    }
}

class LightVehicle extends Vehicle {

    LightVehicle() {
        name = "LightVehicle";
    }
}

public class InstanceOfExample {

    static boolean result;
    static HeavyVehicle hV = new HeavyVehicle();
    static Truck T = new Truck();
    static HeavyVehicle hv2 = null;
    public static void main(String[] args) {
        result = hV instanceof HeavyVehicle;
        System.out.print("hV is an HeavyVehicle: " + result + "\n");
        result = T instanceof HeavyVehicle;
        System.out.print("T is an HeavyVehicle: " + result + "\n");
        result = hV instanceof Truck;
        System.out.print("hV is a Truck: " + result + "\n");
        result = hv2 instanceof HeavyVehicle;
        System.out.print("hv2 is an HeavyVehicle: " + result + "\n");
        hV = T; //Sucessful Cast form child to parent
        T = (Truck) hV; //Sucessful Explicit Cast form parent to child
    }
}

在 T 被分配了参考 hV 和类型转换为 (Truck) 的最后一行中,为什么它在注释中说这是从父到子的成功显式强制转换?据我所知,强制转换(隐式或显式)只会更改声明的对象类型,而不会更改实际类型(除非您实际为该对象的字段引用分配新的类实例,否则不应更改)。如果 hv 已经分配了一个 HeavyVehicle 类的实例,该类是 Truck 类的超类,那么如何将此字段类型转换为从 HeavyVehicle 类扩展而来的更具体的子类 Truck?

我的理解是,强制转换的目的是限制对对象(类实例)的某些方法的访问。因此,不能将对象转换为比对象实际分配的类具有更多方法的更具体的类。这意味着该对象只能被转换为超类或与实际实例化它的类相同的类。这是正确的还是我在这里错了?我还在学习,所以我不确定这是否是看待事物的正确方式。

我也明白这应该是下放的一个例子,但是我不确定如果实际类型没有此对象被下放到的类的方法,这实际上是如何工作的。显式转换是否以某种方式更改了对象的实际类型(而不仅仅是声明的类型),因此此对象不再是 HeavyVehicle 类的实例,而是现在成为 Truck 类的实例?


答案 1

引用、 对象 vs 类型

对我来说,关键是理解对象与其引用之间的差异,或者换句话说,对象与其类型之间的差异。

当我们在Java中创建一个对象时,我们声明了它的真实性质,它永远不会改变(例如)。但是Java中的任何给定对象都可能具有多种类型。其中一些类型显然是由类层次结构给出的,其他类型则不那么明显(即泛型,数组)。new Truck()

特别是对于引用类型,类层次结构规定了子类型规则。例如,在您的示例中,所有卡车都是重型车辆所有重型车辆都是车辆。因此,这种 is-a 关系层次结构决定了卡车具有多种兼容类型

当我们创建一个 时,我们定义一个“引用”来访问它。此引用必须具有这些兼容类型之一。Truck

Truck t = new Truck(); //or
HeavyVehicle hv = new Truck(); //or
Vehicle h = new Truck() //or
Object o = new Truck();

因此,这里的关键点是认识到对对象的引用不是对象本身。正在创建的对象的性质永远不会改变。但是我们可以使用不同类型的兼容引用来访问对象。这是这里多态性的特征之一。可以通过不同“兼容”类型的引用访问同一对象。

当我们进行任何类型的转换时,我们只是假设不同类型的引用之间的这种兼容性的性质。

上放或加宽参考转换

现在,有了类型的引用,我们可以很容易地得出结论,它总是与类型的引用兼容,因为所有卡车都是车辆。因此,我们可以在不使用显式强制转换的情况下对引用进行修正。TruckVehicle

Truck t = new Truck();
Vehicle v = t;

它也被称为加宽引用转换,基本上是因为当你在类型层次结构中向上移动时,类型会变得更加通用。

如果需要,可以在此处使用显式强制转换,但这是不必要的。我们可以看到,被引用的实际对象是相同的。它是,并将永远是.tvTruck

下倾或缩小参考转换

现在,有了一个类型的引用,我们不能“安全地”得出结论,它实际上引用了一个。毕竟,它也可能引用其他形式的车辆。例如VechicleTruck

Vehicle v = new Sedan(); //a light vehicle

如果您在代码中的某个位置找到引用,而不知道它引用了哪个特定对象,则无法“安全地”争论它是否指向或指向任何其他类型的车辆。vTruckSedan

编译器很清楚,它不能对所引用对象的真实性质提供任何保证。但是程序员通过阅读代码,可以确定他/她在做什么。与上面的情况一样,您可以清楚地看到正在引用.Vehicle vSedan

在这些情况下,我们可以做一个向下转换。我们这样称呼它,是因为我们正在沿着类型层次结构向下移动。我们也称之为缩小参考转换。我们可以说

Sedan s = (Sedan) v;

这总是需要一个显式的强制转换,因为编译器不能确定这是安全的,这就是为什么这就像问程序员,“你确定你在做什么吗?如果你对编译器撒谎,当你执行此代码时,你会在运行时抛出你。ClassCastException

其他类型的子类型规则

Java中还有其他子类型规则。例如,还有一个称为数字提升的概念,它会自动强制表达式中的数字。喜欢在

double d = 5 + 6.0;

在本例中,由整数和双精度两种不同类型组成的表达式在计算表达式之前将整数上演/强制转换为双精度值,从而得到双精度值。

您也可以执行原始上放和下放。如

int a = 10;
double b = a; //upcasting
int c = (int) b; //downcasting

在这些情况下,当信息可能丢失时,需要显式强制转换。

某些子类型规则可能不那么明显,例如在数组的情况下。例如,所有引用数组都是 的子类型,但基元数组不是。Object[]

在泛型的情况下,特别是使用通配符如 和 ,事情变得更加复杂。喜欢在superextends

List<Integer> a = new ArrayList<>();
List<? extends Number> b = a;
        
List<Object> c = new ArrayList<>(); 
List<? super Number> d = c;

其中 类型 是 类型的子类型。的类型是 类型的子类型。abcd

使用协方差,无论出现在哪里,都可以传递 ,因此是 的子类型。List<? extends Number>List<Integer>List<Integer>List<? extends Number>

逆变产生类似的效果,无论类型出现在哪里,都可以传递 一个 ,它构成 的子类型。List<? super Number>List<Object>List<Object>List<? super Number>

而且拳击和拆箱也受到一些选角规则的约束(在我看来,这也是某种形式的胁迫)。


答案 2

你做对了。您只能成功地将对象强制转换为其类、其某些父类或它或其父级实现的某些接口。如果将其强制转换为某些父类或接口,则可以将其强制转换为原始类型。

否则(虽然你可以在源代码中使用它),它将导致运行时ClassCastException。

铸造通常用于将不同的东西(相同的接口或父类,例如,您的所有汽车)存储在同一字段或相同类型的集合中(例如。车辆),以便您可以以相同的方式与他们合作。

如果你想获得完全访问权限,你可以把它们扔回去(例如。车辆到卡车)


在示例中,我非常确定最后一个语句是无效的,并且注释是完全错误的。


推荐