将一个来自父类的对象强制转换为子类对象

22
我对Java语言中的类型转换存在误解。问题出在ClassCastException上。例如,在以下代码中,假设Animal是Dog类的父类:

Animal animal = new Animal();
Dog dog = (Dog) animal;

执行后会抛出ClassCastException异常。然而,在学习Android包时,我发现了一个关于类型转换的示例,该示例应该会抛出ClassCastException(考虑到Java示例)

EditText editText = (EditText) findViewById(R.id.edit_message);

在这段代码中,findViewById方法返回一个View类对象,该对象是EditText类的超类之一。从android.view.Viewandroid.widget.EditText。代码可以正常运行。有人能解释一下是我犯了错误还是怎么回事吗?

提前感谢。


1
如果你知道如何使用“多态性”标签来标记这个问题,那么你就知道如何在本网站或其他地方搜索这个概念。说真的,这太基础了。在发布之前先搜索一下。 - Itai Hanski
问题不在于多态机制,我知道这个例子确实包含了多态。真正的问题在于Android的实现。我知道为什么会出现异常,我想要请教有关Android代码的澄清。 - mwb
没有所谓的“Android实现”。将对象视为其祖先父类是多态性的核心,这是面向对象编程的关键方面,因此也是Java(因此也是Android)的关键方面。我的评论试图告诉你,你太急于发布一个基本问题,而这个问题可以通过在SO或Google上进行简单搜索(或彻底阅读developer.android.com)轻松回答。 - Itai Hanski
5个回答

29

一旦你创建了一个对象,你就无法更改它的类型。这就是为什么你不能将动物转换为狗。

但是,如果你创建一个子类的对象,你可以在超类类型的变量中保留对该对象的引用,并且稍后可以将其转换为子类类型。

这样做是可行的:

Animal a = new Dog ();
Dog d = (Dog) a;
在Android示例中,您有一个布局资源,其外观如下:
<EditText
    android:id="@+id/edit_message"
 ..."/>

这个定义会导致 Android 创建一个 EditText 的实例,因此您可以将由 findViewById 返回的视图转换为 EditText。您不能将它转换为任何不是 EditText 的超类型的东西。


好的,我理解这种类型转换的工作原理。然而,安卓示例让我感到困惑。如果我不在子类中实例化对象,我能像在安卓示例中那样将每个超类对象强制转换为子类对象吗?我将编写一个方法来代替直接进行类型转换:“父类”公共动物foo(){Animal animal2 = new Animal();返回animal2;}和“子类”Dog dog = (Dog) animal.foo();这仍然会导致异常。 - mwb
@mwb 不,对于Android示例,Android会根据布局XML中的定义实例化对象。您只能将其强制转换为其正确的子类型。 - Eran
你应该阅读Java中的多态和类型转换,Android中没有发生任何不寻常的事情。 - Vinayak Bhavnani
@mwb 在这种特殊情况下,Android负责实例化,但你会在许多面向对象的平台上遇到这种行为,它们从定义文件中实例化对象。 - Eran

5

基本上,您无法将超类的实例转换为子类,因为尚未知道子类的实例。向上转型是防止出现此异常的可靠方法,因为我们现在正在将多态性引入代码中。

您必须首先创建子类的实例:

Dog dog = new Dog;

我们可以通过将Dog类转换为其父类Animal来隐藏其未被发现的方法。
Animal animal = (Animal) dog;

然后,您可以将其向下转换回子类 Dog,因为其子类的实例已知:

Dog anotherDog = (Dog) animal;

2
你可以尝试这个方法:
Base obj= new Derived();
Derived obj2= (Derived)obj;

希望这能有所帮助。

1
public class OverridingConcept {
    public static void main(String[] args) {
        // Up-Casting
        Parent p = new Child();
        Child c = (Child) p;
        c.eat();
    }
}
class Parent {
    void show() {
        System.out.println("Parent.Show");
    }
    void eat() {
        System.out.println("Parent.Eat");
    }
}
class Child extends Parent {
    void show() {
        System.out.println("Child.Show");
    }
    void sing() {
        System.out.println("Child.Sing");
    }
}

-1
如果你想将父类改为子类,那么请使用下面的代码。
Animal a = new Animal(); Dog d = Dog.valueOf(a);

1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community
Dog类中不存在valueof()方法,这在各个方面都是完全错误的。 - Shavk with a Hoon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接