简体   繁体   English

关于 Java 多态和强制转换的问题

[英]Question about Java polymorphism and casting

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 . ((M) k).getClass()给出K Why is that?这是为什么? It was casted to the more general M !它被投射到更通用的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!谢谢!

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? ((M) k).getClass() 给出 K。这是为什么? It was casted to the more general M!它被投给了更一般的M!

You've casted k to M, but all classes have a getClass() method.您已将 k 强制转换为 M,但所有类都有一个 getClass() 方法。 k's class is always K, regardless of whather you cast its reference to M or not. k 的类始终是 K,无论您是否将其引用转换为 M。 If you cast a Dog to an Animal and ask it what animal it is it'll still answer that it's a dog.如果你把狗扔给动物并问它是什么动物,它仍然会回答它是一只狗。

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() 方法。

((M) k).doIt(); ((M) k).doIt();

gives M's or K's doIt()?给出 M 或 K 的 doIt()?

K's doIt() for the same reasons as above. K 的 doIt() 出于与上述相同的原因。 The cast operates on the reference;强制转换对引用进行操作; it doesn't transform an object to a different type.它不会将对象转换为不同的类型。


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.为此,我们调用只存在于 Dog 上的takeForWalk()方法,或只存在于 Cat 上的play()方法。

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() );
         }
     }
}

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 do change.在这种情况下,值确实会发生变化。

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.编辑:我认为这里的混淆是因为你认为 k 在你施放时会“变成”一个 M,但事实并非如此。 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".你只是把它当作一个M。如果你问一个“狗主人”它是什么品种,他不会返回“这是一只狗”,原因很简单,getBreedName()方法很可能有在子类 LabradorOwner 中被覆盖以返回“拉布拉多”。 It is the same with getClass(), it will return the class of the implementation.与 getClass() 相同,它会返回实现的类。 It will not be an M but a K that happens to be an M as well just because K extends M.它不会是一个 M,而是一个恰好也是一个 M 的 K,仅仅因为 K 扩展了 M。

the int / double is unrelated; int / double是无关的; that is a conversion, not a cast - there is no relationship between int and double .那是转换,而不是强制转换 - intdouble之间没有关系。

Re the question;重新提问; a type's object is fixed at creation.类型的对象在创建时固定。 An object that is a C is not (and can never be) an E .作为C对象不是(并且永远不可能是) E However, you can treat an E as a C , since inheritance represents "is a".但是,您可以E视为C ,因为继承代表“是一个”。 For example:例如:

E e = new E();
C c = e;

Here we still only have one object - simply that the c variable thinks of it as a C , so won't expose methods specific to E (even though the object is an E ).这里我们仍然只有一个对象 - 只是c变量将其视为C ,因此不会公开特定于E方法(即使该对象E )。

If we then add:如果我们再添加:

E secondE = (E) c;

This is a type check;这是一个类型检查; again, we haven't changed the object, but to put c into an E variable requires us to prove to the compiler/runtime that it really is an E .同样,我们没有更改对象,但是将c放入E变量需要我们向编译器/运行时证明它确实是一个E We didn't need this in the first example as it can already prove that any E is also a C .我们在第一个例子中不需要这个,因为它已经可以证明任何E也是C

Likewise, with the getClass() - all the cast does is change how the compiler thinks of the object;同样,使用getClass() - getClass()转换所做的只是改变编译器对对象的看法; you haven't changes the object itself.你没有改变对象本身。 It is still a K .它仍然是一个K

You need to separate variables from objects .您需要将变量对象分开。 The cast is talking about the variables ;演员正在谈论变量 they don't change the object.他们不会改变对象。

To add to Frederik's answer, casting an object to something doesn't change it's type.添加到 Frederik 的答案中,将对象投射到某物不会改变它的类型。 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:此外,对象只能转换为它已经是的类型(编译器当时不知道)这就是永远不会接受不可能的转换的原因:

Integer i = (Integer) new String();

will not compile, because the compiler knows it can't be possible.不会编译,因为编译器知道这是不可能的。

((M) k).getClass() gives K. Why is that? ((M) k).getClass() 给出 K。这是为什么? It was casted to the more general M!它被投给了更一般的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.一个有用的类比(我从 Bill Venners 的网站 artima.com 得到)可能有助于消除混淆,即类和对象之间的区别就像建筑师的蓝图和实际建造的房子之间的区别。 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.您可以根据相同的蓝图建造不止一栋房屋。

How is that relevant to this question?这与这个问题有什么关系? Let's say there's a McMansion blueprint and a McMansionWithHeatedPool blueprint.假设有一个McMansion蓝图和一个McMansionWithHeatedPool蓝图。 A McMansionWithHeatedPool is an extension of a McMansion with a heated pool. McMansionWithHeatedPool是带有温水游泳池的McMansion的扩展。

Now, if you see a real McMansionWithHeatedPool , for that object:现在,如果你看到一个真正的McMansionWithHeatedPool ,对于那个对象:

  1. Conceptually (ie, if you look at the architect's blueprints), you would see that a McMansionWithHeatedPool is clearly also a McMansion .从概念上讲(即,如果您查看架构师的蓝图),您会发现McMansionWithHeatedPool显然也是McMansion Hence the upcast is allowed.因此允许向上转换。 (For the same reason, a McMansion object cannot be type cast into a McMansionWithHeatedPool : no heated pool!) (出于同样的原因,不能将McMansion对象类型转换为McMansionWithHeatedPool :没有加热池!)

  2. (( McMansion ) k).getClass() gives McMansionWithHeatedPool because k is still a McMansionWithHeatedPool . (( McMansion )K).getClass()给出McMansionWithHeatedPool因为k是仍然一个McMansionWithHeatedPool The type cast is on the expression, not on the object.类型转换是在表达式上,而不是在对象上。

"If the compiler treats it as an M, it should execute M's methods." “如果编译器将其视为 M,则它应该执行 M 的方法。”
The compiler treats the reference as M. The instance that 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.编译器将引用视为 M。引用指向的实例类型为 K,而不是 M。您不能强制转换引用并假设这意味着实例会突然改变行为。 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.它与调用哪个实现没有任何关系,只是一个实现确实存在。

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. getClass() 获取内存中对象的类型。 Casting to M simply hides the fact that it's a K, it doesn't change the underlying object.强制转换为 M 只是隐藏了它是 K 的事实,它不会改变底层对象。

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 .例如C extends E And they both have a method myName();他们都有一个方法myName(); . . If you say如果你说

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 E with C reference type.您只是告诉编译器它应该允许您使用C引用类型来引用E

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM