关于 Java 多态和强制转换的问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1082453/
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
Question about Java polymorphism and casting
提问by ooboo
I have a class C. Class E extends it.
我有一个 C 类。 E 类扩展了它。
E e = new E();
C c = new C();
Why is
为什么是
e = (E) c;
Upon further review: though numeric conversions have the same syntax as casting objects, some confusion arose. At any event, the above does not give a compilation, but rather a runtime error - so a class can be casted to subclass in some instances (otherwise the code would not compile). Any examples that anyone can give where the above works?
经过进一步:尽管数字转换与强制转换对象具有相同的语法,但还是出现了一些混淆。在任何情况下,以上都没有给出编译,而是一个运行时错误 - 因此在某些情况下可以将类转换为子类(否则代码将无法编译)。任何人都可以给出上述工作的任何例子?
And also:
并且:
K extends M
K k = new K();
((M) k).getClass()gives K. Why is that? It was casted to the more general M!
((M) k).getClass()给K. 这是为什么?就被投的更一般了M!
Suppose I have a doIt() method implemented in both M and K. executing
假设我在 M 和 K 中都实现了一个 doIt() 方法。
((M) k).doIt();
gives M's or K's doIt()?
给出 M 或 K 的 doIt()?
Thanks!
谢谢!
回答by banjollity
Consider a real-world example:
考虑一个真实世界的例子:
public class Dog extends Animal
All dogs are animals, but not all animals are dogs. Hence...
所有的狗都是动物,但并非所有的动物都是狗。因此...
public class Cat extends Animal
Casting an Animal to a Dog can only be done if the Animal in question is indeed a Dog. Otherwise it would force the Universe to infer properties unique to a dog (wagging tail, barking, etc.) onto an Animal. That Animal might well be a Cat with properties unique to it (purring, rigorous regime of self-cleaning, etc.). If the cast is not possible then a ClassCastException is thrown at runtime.
只有当所讨论的动物确实是狗时,才能将动物投射到狗身上。否则,它会迫使宇宙将狗独有的属性(摇尾巴、吠叫等)推断为动物。那只动物很可能是一只具有独特属性的猫(发出咕噜声、严格的自洁机制等)。如果无法进行转换,则在运行时抛出 ClassCastException。
Nobody wants a dog that purrs.
没有人想要一只会发出咕噜声的狗。
((M) k).getClass() gives K. Why is that? It was casted to the more general M!
((M) k).getClass() 给出 K。这是为什么?它被投给了更一般的M!
You've casted k to M, but all classes have a getClass() method. k's class is always K, regardless of whather you cast its reference to M or not. If you cast a Dog to an Animal and ask it what animal it is it'll still answer that it's a dog.
您已将 k 转换为 M,但所有类都有一个 getClass() 方法。k 的类始终是 K,无论您是否将其引用转换为 M。如果你把狗扔给动物并问它是什么动物,它仍然会回答它是一只狗。
In fact, casting to a superclass is redundant. A Dog already is an Animal and it has all the methods of an Animal as well as its own. Many Code Analysis tools such as FindBugs will notify you of redundant casts so you can remove them.
事实上,转换到超类是多余的。狗已经是动物,它拥有动物的所有方法以及它自己的方法。许多代码分析工具(例如 FindBugs)会通知您多余的强制转换,以便您可以删除它们。
Suppose I have a doIt() method implemented in both M and K. executing
((M) k).doIt();
gives M's or K's doIt()?
假设我在 M 和 K 中都实现了一个 doIt() 方法。
((M) k).doIt();
给出 M 或 K 的 doIt()?
K's doIt() for the same reasons as above. The cast operates on the reference; it doesn't transform an object to a different type.
K 的 doIt() 出于与上述相同的原因。强制转换对引用进行操作;它不会将对象转换为不同的类型。
Can you give an example of when casting (Dog doggy = (Dog) myAnimal) makes sense?
你能举一个例子说明什么时候转换 (Dog doggy = (Dog) myAnimal) 有意义吗?
Sure can. Imagine a method that receives a list of animals for processing. All the dogs need to be taken for a walk, and all the cats need to be played with using a bird-shaped toy. To do this we call the takeForWalk()method that only exists on Dog, or the play()method which only exists on Cat.
当然可以。想象一个接收动物列表以进行处理的方法。所有的狗都需要带去散步,所有的猫都需要用鸟形玩具玩耍。为此,我们调用takeForWalk()只存在于 Dog 上的play()方法,或只存在于 Cat 上的方法。
public void amuseAnimals( List<Animal> animals ) {
for ( Animal animal : animals ) {
if ( animal instanceof Dog ) {
Dog doggy = (Dog)animal;
doggy.takeForWalk( new WalkingRoute() );
} else if ( animal instanceof Cat ) {
Cat puss = (Cat)animal;
puss.play( new BirdShapedToy() );
}
}
}
回答by Joachim Sauer
You can't cast objects in Java.
您不能在 Java 中转换对象。
You can cast references in Java.
您可以在 Java 中转换引用。
Casting a reference doesn't change anything about the object it refers to. It only produces a reference of a different type pointing to the same object as the initial reference.
转换引用不会改变它所引用的对象的任何内容。它只生成指向与初始引用相同的对象的不同类型的引用。
Casting primitive values is different from casting references. In this case the values dochange.
转换原始值与转换引用不同。在这种情况下,值确实会发生变化。
回答by Fredrik
Just because E extends C, C doesn't become an E... E on the other hand is a C
仅仅因为 E 扩展了 C,C 不会成为 E... E 是 C
Edit:To expand Mark's comment below... Just because every woman is a human, not all humans are women. All humans share the "human interface" with legs, hands, faces, etc. Women extends it with functionality that returns good feelings when you provide diamonds and gold.
编辑:扩大马克在下面的评论......仅仅因为每个女人都是人,并不是所有的人都是女人。所有人类都共享与腿、手、脸等的“人机界面”。女性通过功能扩展它,当您提供钻石和黄金时,这些功能会返回良好的感觉。
The int => double conversion is not even related as it is not a class cast but a conversion telling the compiler to store whatever is in x in y (which happens to be a double).
int => double 转换甚至都不相关,因为它不是类转换,而是一种转换,它告诉编译器将 x 中的任何内容存储在 y 中(恰好是一个双精度值)。
((M) k).getClass() gives K.
((M) k).getClass() 给出 K。
because k is still a K even if you cast it to an M or an Object (or something else it happens to be).
因为即使将 k 转换为 M 或对象(或碰巧是其他东西), k 仍然是 K。
Edit:I think the confusion here is because you consider k to "become" an M when you cast it, it doesn't. You are just treating it as an M. If you ask someone who is a "dog owner" what kind of breed it is he will not return "It's a dog", the reason is simply that the getBreedName() method is likely to have been overridden in the subclass LabradorOwner to return "Labrador". It is the same with getClass(), it will return the class of the implementation. It will not be an M but a K that happens to be an M as well just because K extends M.
编辑:我认为这里的混淆是因为你认为 k 在你施放时会“变成”一个 M,但事实并非如此。你只是把它当作一个M。如果你问一个“狗主人”它是什么品种,他不会返回“It's a dog”,原因很简单,getBreedName()方法很可能有在子类 LabradorOwner 中被覆盖以返回“拉布拉多”。与 getClass() 相同,它会返回实现的类。它不会是一个 M,而是一个恰好也是一个 M 的 K,仅仅因为 K 扩展了 M。
回答by Marc Gravell
the int/doubleis unrelated; that is a conversion, not a cast - there is no relationship between intand double.
的int/double无关; 那是转换,而不是强制转换 -int和之间没有关系double。
Re the question; a type's object is fixed at creation. An objectthat is a Cis not(and can never be) an E. However, you can treatan Eas a C, since inheritance represents "is a". For example:
重新提问;类型的对象在创建时固定。作为a的对象C不是(并且永远不可能是) an E。但是,您可以将anE视为 a C,因为继承代表“是 a”。例如:
E e = new E();
C c = e;
Here we still only have one object - simply that the cvariable thinks of it as a C, so won't expose methods specific to E(even though the object isan E).
这里我们仍然只有一个对象 - 只是c变量将其视为 a C,因此不会公开特定于的方法E(即使对象是an E)。
If we then add:
如果我们再添加:
E secondE = (E) c;
This is a type check; again, we haven't changed the object, but to put cinto an Evariable requires us to prove to the compiler/runtime that it really isan E. We didn't need this in the first example as it can already prove that any Eis also a C.
这是一个类型检查;再次,我们没有改变的对象,而是把c到E变量需要我们去证明编译器/运行时,它确实是一个E。我们在第一个例子中不需要这个,因为它已经可以证明 anyE也是一个C.
Likewise, with the getClass()- all the cast does is change how the compiler thinks of the object; you haven't changes the object itself. It is still a K.
同样,使用getClass()- 所有类型转换所做的就是改变编译器对对象的看法;你没有改变对象本身。它仍然是一个K.
You need to separate variablesfrom objects. The cast is talking about the variables; they don't change the object.
您需要将变量与对象分开。演员正在谈论变量;他们不会改变对象。
回答by vijucat
((M) k).getClass() gives K. Why is that? It was casted to the more general M!
((M) k).getClass() 给出 K。这是为什么?它被投给了更一般的M!
A useful analogy (that I got from Bill Venners' site artima.com) that may help clear the confusion is that the difference between classes and objects is like the difference between an architect's blueprint and the actual house that is built. The blueprint exists on paper and is a concept, while the house exists in real life. You can have more than one house built to the same blueprint.
一个有用的类比(我从 Bill Venners 的网站 artima.com 得到)可能有助于消除混淆,即类和对象之间的区别就像建筑师的蓝图和实际建造的房子之间的区别。蓝图存在于纸上,是一个概念,而房子存在于现实生活中。您可以根据相同的蓝图建造不止一栋房屋。
How is that relevant to this question? Let's say there's a McMansionblueprint and a McMansionWithHeatedPoolblueprint. A McMansionWithHeatedPoolis an extension of a McMansionwith a heated pool.
这与这个问题有什么关系?假设有一个McMansion蓝图和一个McMansionWithHeatedPool蓝图。AMcMansionWithHeatedPool是McMansion带温水池的延伸。
Now, if you see a real McMansionWithHeatedPool, for that object:
现在,如果您看到McMansionWithHeatedPool该对象的真实, :
Conceptually (i.e., if you look at the architect's blueprints), you would see that a
McMansionWithHeatedPoolis clearly also aMcMansion. Hence the upcast is allowed. (For the same reason, aMcMansionobject cannot be type cast into aMcMansionWithHeatedPool: no heated pool!)((
McMansion) k).getClass() givesMcMansionWithHeatedPoolbecause k is still aMcMansionWithHeatedPool. The type cast is on the expression, not on the object.
从概念上讲(即,如果您查看架构师的蓝图),您会发现 a
McMansionWithHeatedPool显然也是 aMcMansion。因此允许向上转换。(出于同样的原因,McMansion不能将对象类型转换为McMansionWithHeatedPool:没有加热池!)((
McMansion) k).getClass() 给出,McMansionWithHeatedPool因为 k 仍然是 aMcMansionWithHeatedPool。类型转换是在表达式上,而不是在对象上。
回答by vijucat
"If the compiler treats it as an M, it should execute M's methods."
The compiler treats the referenceas M. The instancethat the reference points to is of type K, not M. You can't cast the reference and assume that means the instance will suddenly change behavior. What the compiler does is make sure that the method you invoke on the specified reference exists. It does not have anything to do with which implementation is invoked, only that an implementation does exist.
“如果编译器将其视为 M,则它应该执行 M 的方法。”
编译器将引用视为 M。引用指向的实例属于 K 类型,而不是 M。您不能强制转换引用并假设这意味着实例会突然改变行为。编译器所做的是确保您对指定引用调用的方法存在。它与调用哪个实现没有任何关系,只是一个实现确实存在。
回答by Jorn
To add to Frederik's answer, casting an object to something doesn't change it's type. Also, object's can only be cast to a type it already is (the compiler just doesn't know at that point) That's why impossible casts will never be accepted:
添加到 Frederik 的答案中,将对象投射到某物不会改变它的类型。此外,对象只能转换为它已经是的类型(编译器当时不知道)这就是永远不会接受不可能的转换的原因:
Integer i = (Integer) new String();
will not compile, because the compiler knows it can't be possible.
不会编译,因为编译器知道这是不可能的。
回答by Elvis chidera
Casting an object does not change the object to the object being cast, but allows another class reference that is related to it by inheritance to refer to the object.
强制转换对象不会将对象更改为被强制转换的对象,而是允许通过继承与其相关的另一个类引用来引用该对象。
For example C extends E. And they both have a method myName();. If you say
例如C extends E。而且他们都有一个方法myName();。如果你说
E e = new C();
e.myName();
you are calling C myName()method and if you also say
你正在调用C myName()方法,如果你也说
E e = new E();
C c = (C)e;
you just told the compiler that it should allow you refer to Ewith Creference type.
您只是告诉编译器它应该允许您E使用C引用类型进行引用。
回答by Sean
For the first question, you cannot cast a superclass to a subclass because a subclass adds members that the superclass doesn't have. How is the compiler supposed to know what values to put there when it's casting? Basically, an E is a C, but a C is not an E.
对于第一个问题,您不能将超类转换为子类,因为子类添加了超类没有的成员。编译器如何知道在转换时要放置哪些值?基本上,E是C,但C不是E。
getClass() gets the type of the object in memory. Casting to M simply hides the fact that it's a K, it doesn't change the underlying object.
getClass() 获取内存中对象的类型。强制转换为 M 只是隐藏了它是 K 的事实,它不会改变底层对象。

