java中的显式类型转换示例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20096297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:07:32  来源:igfitidea点击:

explicit type casting example in java

javacastingdowncast

提问by SineLaboreNihil

I have come across this example on http://www.javabeginner.com/learn-java/java-object-typecastingand in the part where it talks about explicit type casting there is one example which confuses me.

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

The example:

这个例子:

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
    }
}

In the last line where T is assigned the reference hV and typecast as (Truck), why does it say in the comment that this is a Successful Explicit Cast from parent to child? As I understand casting (implicit or explicit) will only change the declared type of object, not the actual type (which shouldn't ever change, unless you actually assign a new class instance to that object's field reference). If hv was already assigned an instance of a HeavyVehicle class which is a super class of the Truck class, how can then this field be type cast into a more specific subclass called Truck which extends from the HeavyVehicle class?

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

The way I understand it is that casting serves the purpose of limiting access to certain methods of an object (class instance). Therefore you can't cast an object as a more specific class which has more methods then the object's actual assigned class. That means that the object can only be cast as a superclass or the same class as the class from which it was actually instantiated. Is this correct or am I wrong here? I am still learning so I am not sure if this is the correct way of looking at things.

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

I also understand that this should be an example of downcasting, but I am not sure how this actually works if the actual type doesn't have the methods of the class to which this object is being downcasted. Does explicit casting somehow change the actual type of object (not just the declared type), so that this object is no longer an instance of HeavyVehicle class but now becomes an instance of Truck class?

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

采纳答案by Edwin Dalorzo

Reference vs Object vs Types

引用 vs 对象 vs 类型

The key, for me, is understanding the difference between an object and its references, or put in other words the difference between an object and its types.

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

When we create an object in Java, we declare its true nature, which will never change. But any given object in Java is likely to have multiple types.Some of these types are obviously given thanks to the class hierarchy, others are not so obvious (i.e. generics, arrays).

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

Specifically for reference types, the class hierarchy dictates the subtyping rules. For instance in your example all trucks are heavy vehicles, and all heavy vehicles are vehicles. Therefore, this hierarchy of is-a relationships dictates that a truck has multiple compatible types.

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

When we create a Truck, we define a "reference" to get access to it. This reference must have one of those compatible types.

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

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

So the key point here is the realization that the reference to the object is not the object itself. The nature of the object being created is never going to change. But we can use different kinds of compatible references to gain access to the object. This is one of the features of polymorphism here. The same object may be accessed through references of different "compatible" types.

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

When we do any kind of casting, we are simply assuming the nature of this compatibility between different types of references.

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

Upcasting or Widening Reference Conversion

向上转换或扩大参考转换

Now, having a reference of type Truck, we can easily conclude that it's always compatible with a reference of type Vehicle, because all Trucks are Vehicles. Therefore, we could upcast the reference, without using an explicit cast.

现在,有了 type 的引用Truck,我们可以很容易地得出结论,它始终与 type 的引用兼容Vehicle,因为所有 Trucks 都是 Vehicles。因此,我们可以向上转换引用,而不使用显式转换。

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

It is also called a widening reference conversion, basically because as you go up in the type hierarchy, the type gets more general.

它也被称为扩展引用转换,基本上是因为随着类型层次结构的上升,类型变得更加通用。

You could use an explicit cast here if you wanted, but it would be unnecessary. We can see that the actual object being referenced by tand vis the same. It is, and will always be a Truck.

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

Downcasting or Narrowing Reference Conversion

向下转换或缩小参考转换

Now, having a reference of type Vechiclewe cannot "safely" conclude that it actually references a Truck. After all it may also reference some other form of Vehicle. For instance

现在,有了一个类型的引用,Vechicle我们不能“安全地”断定它实际上引用了Truck. 毕竟它也可能引用其他形式的 Vehicle。例如

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

If you find the vreference somewhere in your code without knowing to which specific object it is referencing, you cannot "safely" argument whether it points to a Truckor to a Sedanor any other kind of vehicle.

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

The compiler knows well that it cannot give any guarantees about the true nature of the object being referenced. But the programmer, by reading the code, may be sure of what s/he is doing. Like in the case above, you can clearly see that Vehicle vis referencing a Sedan.

编译器很清楚它不能保证所引用对象的真实性质。但是程序员通过阅读代码,可以确定他/她在做什么。就像上面的例子一样,你可以清楚地看到它Vehicle v引用了Sedan.

In those cases, we can do a downcast. We call it that way because we are going down the type hierarchy. We also call this a narrowing reference conversion. We could say

在这些情况下,我们可以做一个低头。我们这样称呼它是因为我们要沿着类型层次结构向下移动。我们也称其为收缩引用转换。我们可以说

Sedan s = (Sedan) v;

This always requires an explicit cast, because the compiler cannot be sure this is safe and that's why this is like asking the programmer, "are you sure of what you are doing?". If you lie to the compiler you will get a ClassCastExceptionat run time, when this code is executed.

这总是需要显式转换,因为编译器无法确定这是安全的,这就是为什么这就像问程序员,“你确定你在做什么吗?”。如果你对编译器撒谎,你会ClassCastException在运行时得到一个,当这段代码被执行时。

Other Kinds of Subtyping Rules

其他类型的子类型规则

There are other rules of subtyping in Java. For instance, there is also a concept called numeric promotion that automatically coerce numbers in expressions. Like in

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

double d = 5 + 6.0;

In this case an expression composed of two different types, integer and double, upcasts/coerces the integer to a double before evaluating the expression, resulting in a double value.

在这种情况下,由两种不同类型(整数和双精度)组成的表达式在计算表达式之前将整数向上转换/强制转换为双精度,从而产生双精度值。

You may also do primitive upcasting and downcasting. As in

你也可以做原始的向上转换和向下转换。如

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

In these cases, an explicit cast is required when information can be lost.

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

Some subtyping rules may not be so evident, like in the cases of arrays. For instance, all reference arrays are subtypes of Object[], but primitive arrays are not.

一些子类型规则可能不那么明显,就像在数组的情况下一样。例如,所有引用数组都是 的子类型Object[],但原始数组不是。

And in the case of generics, particularly with the use of wildcards like superand extends, things get even more complicated. Like in

而在泛型的情况下,特别是使用像super和这样的通配符时extends,事情变得更加复杂。像

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

List<Object> c = new ArrayList<>(); 
List<? super Number> d = c;

Where the type of bis a subtype of the type of a. And the type of dis a subtype of the type of c.

其中 的类型b是 类型的子类型a。而 的类型d是 的类型的子类型c

And also boxing and unboxing are subject to some casting rules (yet again this is also some form of coercion in my opinion).

而且装箱和拆箱也受制于一些铸造规则(在我看来,这也是某种形式的强制)。

回答by MightyPork

You got it right. You can successfully cast an object only to its class, some of its parent classes or to some interface it or its parents implement. If you casted it to some of the parent classes or interfaces, you can cast it back to the original type.

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

Otherwise (while you can have it in source), it will result in a runtime ClassCastException.

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

Casting is typically used to make it possible to store different things (of the same interface or parent class, eg. all your cars) in the same field or a collection of the same type (eg. Vehicle), so that you can work with them the same way.

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

If you then want to get the full access, you can cast them back (eg. Vehicle to Truck)

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



In the example, I am pretty sure that the last statement is invalid and the comment is simply wrong.

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

回答by David SN

When you make a cast from a Truck object to a HeavyVehicle like that:

当您像这样从 Truck 对象转换为 HeavyVehicle 时:

Truck truck = new Truck()
HeavyVehicle hv = truck;

The object is still a truck, but you only have access to heavyVehicle methods and fields using the HeavyVehicle reference. If you downcast to a truck again, you can use again all the truck methods and fields.

该对象仍然是一辆卡车,但您只能使用 HeavyVehicle 引用访问 HeavyVehicle 方法和字段。如果您再次沮丧到卡车,您可以再次使用所有卡车方法和字段。

Truck truck = new Truck()
HeavyVehicle hv = truck;
Truck anotherTruckReference = (Truck) hv; // Explicit Cast is needed here

If the actual object you are downcasting is not a truck, a ClassCastException will be throw like in the following example:

如果您向下转换的实际对象不是卡车,则会抛出 ClassCastException,如下例所示:

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

The exception is thrown because the actual object is not of the correct class, its an object of a superclass (HeavyVehicle)

抛出异常是因为实际对象不属于正确的类,它是超类 (HeavyVehicle) 的对象

回答by Prashant Thakkar

The above code will compile and run fine. Now change above code and add following line System.out.println(T.name);

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

This will make sure that you are not using the object T after downcasting hV object as Truck.

这将确保您在将 hV 对象向下转换为 Truck 后不使用对象 T。

Currently, in your code you are not using T after downcast so everything is fine and working.

目前,在您的代码中,您在向下转型后没有使用 T,因此一切正常且工作正常。

This is because, by explicitly cast hV as Truck, complier does complain considering that programmer as casted the object and is aware of the what object is been casted to what.

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

But at runtime JVM is not able to justify the casting and throws ClassCastException "HeavyVehicle cannot be cast to Truck".

但是在运行时 JVM 无法证明转换的合理性并抛出 ClassCastException “HeavyVehicle cannot be cast to Truck”。

回答by dshiga

The last line of code compiles and runs successfully with no exceptions. What it does is perfectly legal.

最后一行代码编译并成功运行,没有异常。它的所作所为是完全合法的。

  1. hV initially refers to an object of type HeavyVehicle (let's call this object h1):

    static HeavyVehicle hV = new HeavyVehicle(); // hV now refers to h1.
    
  2. Later, we make hV refer to a different object, of type Truck (let's call this object t1):

    hV = T; // hV now refers to t1.
    
  3. Lastly, we make T refer to t1.

    T = (Truck) hV; // T now refers to t1.
    
  1. hV 最初指的是一个 HeavyVehicle 类型的对象(我们称这个对象为 h1):

    static HeavyVehicle hV = new HeavyVehicle(); // hV now refers to h1.
    
  2. 稍后,我们让 hV 指向一个不同的对象,类型为 Truck(我们称这个对象为 t1):

    hV = T; // hV now refers to t1.
    
  3. 最后,我们让 T 指代 t1。

    T = (Truck) hV; // T now refers to t1.
    

T already referred to t1, so this statement didn't change anything.

T 已经引用了 t1,所以这个语句没有改变任何东西。

If hv was already assigned an instance of a HeavyVehicle class which is a super class of the Truck class, how can then this field be type cast into a more specific subclass called Truck which extends from the HeavyVehicle class?
如果已经为 hv 分配了一个 HeavyVehicle 类的实例,该类是 Truck 类的超类,那么如何将这个字段类型转换为一个更具体的子类,称为 Truck,它从 HeavyVehicle 类扩展而来?

By the time we reach the last line, hV no longer refers to an instance of HeavyVehicle. It refers to an instance of Truck. Casting an instance of Truck to type Truck is no problem.

当我们到达最后一行时,hV 不再是指 HeavyVehicle 的实例。它指的是 Truck 的一个实例。将 Truck 的实例转换为 Truck 类型没有问题。

That means that the object can only be cast as a superclass or the same class as the class from which it was actually instantiated. Is this correct or am I wrong here?
这意味着该对象只能被转换为超类或与实际实例化它的类相同的类。这是正确的还是我错了?

Basically, yes, but don't confuse the object itself with a variable that refers to the object. See below.

基本上,是的,但不要将对象本身与引用该对象的变量混淆。见下文。

Does explicit casting somehow change the actual type of object (not just the declared type), so that this object is no longer an instance of HeavyVehicle class but now becomes an instance of Truck class?
显式转换是否以某种方式改变了对象的实际类型(不仅仅是声明的类型),以便该对象不再是 HeavyVehicle 类的实例,而是现在成为 Truck 类的实例?

No. An object, once created, can never change its type. It can't become an instance of another class.

不可以。对象一旦创建,就永远不能改变它的类型。它不能成为另一个类的实例。

To reiterate, nothing changed on the last line. T referred to t1 before that line and it refers to t1 afterward.

重申一下,最后一行没有任何变化。T 在该行之前指代 t1,在之后指代 t1。

So why is the explicit cast (Truck) necessary on the last line? We are basically helping just helping out the compiler.

那么为什么最后一行需要显式转换 (Truck) 呢?我们基本上只是帮助编译器。

Weknow that by that point, hV refers to an object of type Truck, so it's ok to assign that object of type Truck to the variable T. But the compilerisn't smart enough to know that. The compiler wants our assurance that when it gets to that line and tries to make the assignment, it will find an instance of Truck waiting for it.

到那时,我们知道 hV 指的是 Truck 类型的对象,因此可以将 Truck 类型的对象分配给变量 T。但是编译器不够聪明,无法知道这一点。编译器希望我们保证,当它到达该行并尝试进行分配时,它将找到一个 Truck 的实例等待它。

回答by S. W. Chi

To help better illustrate some points made above, I modified the code in question and add more codes to it with inline comments (including actual outputs) as follows:

为了更好地说明上面提出的一些观点,我修改了有问题的代码,并使用内联注释(包括实际输出)向其中添加了更多代码,如下所示:

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

        }

}