Java中的显式类型转换示例

Java中的显式类型转换示例,java,casting,downcast,Java,Casting,Downcast,我在讨论显式类型转换时遇到了这个例子,其中有一个例子让我感到困惑 例如: class Vehicle { String name; Vehicle() { name = "Vehicle"; } } class HeavyVehicle extends Vehicle { HeavyVehicle() { name = "HeavyVehicle"; } } class Truck extends HeavyVehi

我在讨论显式类型转换时遇到了这个例子,其中有一个例子让我感到困惑

例如:

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类的实例?

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

否则(虽然您可以将其保存在源代码中),它将导致运行时ClassCastException

强制转换通常用于在同一字段或同一类型的集合(例如车辆)中存储不同的内容(相同接口或父类,例如所有车辆),以便您可以以相同的方式使用它们

如果您想获得完全访问权限,您可以将其退回(例如,从车辆到卡车)



在本例中,我非常确定最后一条语句是无效的,注释是错误的。

当您将卡车对象转换为重型车辆时,如下所示:

Truck truck = new Truck()
HeavyVehicle hv = truck;
该对象仍然是一辆卡车,但您只能使用heavyVehicle引用访问heavyVehicle方法和字段。如果再次向下转换到卡车,则可以再次使用所有卡车方法和字段

Truck truck = new Truck()
HeavyVehicle hv = truck;
Truck anotherTruckReference = (Truck) hv; // Explicit Cast is needed here
如果要向下投射的实际对象不是卡车,则将抛出ClassCastException,如下例所示:

HeavyVehicle hv = new HeavyVehicle();
Truck tr = (Truck) hv;  // This code compiles but will throw a ClasscastException

引发异常是因为实际对象不是正确的类,它是超类(HeavyVehicle)的对象。

上述代码将正常编译和运行。现在更改上面的代码并添加以下行 System.out.println(T.name)

这将确保在将hV对象向下投射为卡车后,不会使用对象T

目前,在您的代码中,在downcast之后您没有使用T,所以一切都很好并且正常工作

这是因为,通过显式地将hV转换为Truck,编译器确实抱怨程序员将对象转换为casted,并且知道将什么对象转换为什么对象


但在运行时,JVM无法证明强制转换的合理性,并抛出ClassCastException“HeavyVehicle无法强制转换为Truck”。

参考与对象vs类型

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

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

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

Truck t = new Truck(); //or
HeavyVehicle hv = new Truck(); //or
Vehicle h = new Truck() //or
Object o = new Truck();
当我们创建
卡车时,我们定义了一个“引用”来访问它。此引用必须具有这些兼容类型之一

Truck t = new Truck(); //or
HeavyVehicle hv = new Truck(); //or
Vehicle h = new Truck() //or
Object o = new Truck();
所以这里的关键点是认识到对对象的引用不是对象本身。被创建对象的性质永远不会改变。但是我们可以使用不同类型的兼容引用来访问对象。这是多态性的特征之一。同一对象可以通过不同“兼容”类型的引用进行访问

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

向上投射或加宽参考转换

现在,有了类型
Truck
的引用,我们可以很容易地得出结论,它总是与类型
Vehicle
的引用兼容,因为所有的卡车都是车辆。因此,我们可以向上转换引用,而不使用显式转换

Truck t = new Truck();
Vehicle v = t;
它也被称为a,基本上是因为当您在类型层次结构中上升时,类型变得更加通用

如果需要,可以在这里使用显式强制转换,但这是不必要的。我们可以看到,
t
v
引用的实际对象是相同的。它是,并且将始终是
卡车

向下广播或缩小参考转换
Sedan s = (Sedan) v;
double d = 5 + 6.0;
int a = 10;
double b = a; //upcasting
int c = (int) b; //downcasting
List<Integer> a = new ArrayList<>();
List<? extends Number> b = a;
        
List<Object> c = new ArrayList<>(); 
List<? super Number> d = c;
static HeavyVehicle hV = new HeavyVehicle(); // hV now refers to h1.
hV = T; // hV now refers to t1.
T = (Truck) hV; // T now refers to t1.
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 a HeavyVehicle: " + result + "\n"); // true

                result = T instanceof HeavyVehicle;
                System.out.print("T is a HeavyVehicle: " + result + "\n"); // true
//      But the following is in error.              
//      T = hV; // error - HeavyVehicle cannot be converted to Truck because all hV's are not trucks.                               

                result = hV instanceof Truck;
                System.out.print("hV is a Truck: " + result + "\n"); // false               

                hV = T; // Sucessful Cast form child to parent.
                result = hV instanceof Truck; // This only means that hV now points to a Truck object.                            
                System.out.print("hV is a Truck: " + result + "\n");    // true         

                T = (Truck) hV; // Sucessful Explicit Cast form parent to child. Now T points to both HeavyVehicle and Truck. 
                                // And also hV points to both Truck and HeavyVehicle. Check the following codes and results.
                result = hV instanceof Truck;                             
                System.out.print("hV is a Truck: " + result + "\n");    // true 

                result = hV instanceof HeavyVehicle;
                System.out.print("hV is a HeavyVehicle: " + result + "\n"); // true             

                result = hV instanceof HeavyVehicle;
                System.out.print("hV is a HeavyVehicle: " + result + "\n"); // true 

                result = hv2 instanceof HeavyVehicle;               
                System.out.print("hv2 is a HeavyVehicle: " + result + "\n"); // false

        }

}