java中的显式类型转换示例

Sin*_*hil 31 java casting downcast

我在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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Edw*_*rzo 60

引用vs对象与类型

对我而言,关键是理解对象与其引用之间的区别,或者换句话说,对象与其类型之间的区别.

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

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

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

Truck t = new Truck(); //or
HeavyVehicle hv = new Truck(); //or
Vehicle h = new Truck() //or
Object o = new Truck();
Run Code Online (Sandbox Code Playgroud)

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

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

上传或扩展参考转换

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

Truck t = new Truck();
Vehicle v = t;
Run Code Online (Sandbox Code Playgroud)

它也被称为扩展引用转换,主要是因为当你进入类型层次结构时,类型变得更加通用.

如果你愿意,可以在这里使用显式转换,但这是不必要的.我们可以看到实际的对象被引用t,并v是一样的.它是,而且永远是一个Truck.

向下转换或缩小参考转换

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

Vehicle v = new Sedan(); //a light vehicle
Run Code Online (Sandbox Code Playgroud)

如果您v在代码中的某个位置找到引用而不知道它引用的是哪个特定对象,则无法"安全地"论证它是指向一个Truck或一个Sedan或任何其他类型的车辆.

编译器很清楚它无法保证被引用对象的真实性质.但程序员通过阅读代码可能会确定他/她在做什么.与上面的情况一样,您可以清楚地看到它Vehicle v引用了一个Sedan.

在这种情况下,我们可以做一个沮丧.我们这样称呼它是因为我们正在进行类型层次结构.我们还将此称为缩小参考转换.我们可以说

Sedan s = (Sedan) v;
Run Code Online (Sandbox Code Playgroud)

这总是需要一个显式的强制转换,因为编译器无法确定这是否安全,这就是为什么这就像问程序员"你确定你在做什么吗?".如果你撒谎到编译器,你将ClassCastException在执行此代码时获得运行时.

其他种类的子类型规则

Java中还有其他子类型规则.例如,还有一个名为数字促销的概念会自动强制表达式中的数字.像

double d = 5 + 6.0;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,表达式由两个不同的类型(整数和双精度)组成,在估计表达式之前将整数转换/强制转换为double,从而得到double值.

您也可以进行原始的向上转换和向下转换.如在

int a = 10;
double b = a; //upcasting
int c = (int) b; //downcasting
Run Code Online (Sandbox Code Playgroud)

在这些情况下,当信息丢失时,需要进行显式转换.

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

而在仿制药的情况下,特别是采用类似于通配符superextends,事情变得更加复杂.像

List<Integer> a = new ArrayList<>();
List<? extends Number> b = a;

List<Object> c = new ArrayList<>(); 
List<? super Number> d = c;
Run Code Online (Sandbox Code Playgroud)

其中类型b是类型的子类型a.并且类型d是类型的子类型c.

而拳击和拆箱也受到一些施法规则的约束(在我看来,这也是某种形式的强制).

  • +1这真的帮助我理解'何时'我应该使用显式铸造. (7认同)

Mig*_*ork 5

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

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

通常使用强制转换来在同一个字段或相同类型的集合(例如,Vehicle)中存储不同的东西(相同的接口或父类,例如所有汽车),以便您可以使用他们以同样的方式.

如果您希望获得完全访问权限,则可以将其强制转换(例如,Vehicle to Truck)


在这个例子中,我很确定最后一个语句是无效的,注释完全错误.