为什么在Java中超类对象不能隐式转换为子类对象?

4
我有以下代码:
class A {
}

class B extends A {
    public void fB(){};
}

根据Java规则:
情况1:
B b = new B();
A a = b;
((B)a).fB();

案例2:
A a = new A();
B b = a;

根据Java规则,Case 1是可以的,而Case 2是不可以的。为什么Case 2 不可以?这条线((B)a).fB();实际上做了什么(我的意思是里面发生了什么)?

4
问题的表述是错误的:子类类型的对象可以隐式地转换为超类类型,并且通常会这样做。而反过来则不行。 - biziclop
你必须反过来思考:系统无法知道 A 是否支持 B 的某个特性 - A 只是 B 提供的特性子集。 - home
为他修正了标题。 - user207421
4个回答

14
因为一只 Dog 是一种 Animal,但不是每种 Animal 都是一只 Dog,所以情况1是正确的而情况2是错误的。
class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }

Dog fifi = new Dog();
Cat tommy = new Cat();

// OK because Dogs and Cats are Animals
Animal pet1 = fifi;
Animal pet2 = tommy;

// Not OK because an Animal is not always a Dog
Dog doggy = pet2;

请注意,强制类型转换并不会对对象进行任何操作;特别地,它不会对对象进行任何类型的转换。强制类型转换只是告诉编译器:“我在这里有一个对象,并且我比你更了解它是什么;我想让你将其视为X类型,而不给我任何错误信息。”

因此在像这样的一行代码中:

Dog doggy = pet2;

编译器会抱怨,因为它无法确定pet2是否实际上是一只Dog; 它只知道它是一个Animal - 并不是所有的Animal都是Dog。 您可以进行强制类型转换,以告诉编译器不要再抱怨:

// Tell the compiler that you want it to treat pet2 as a Dog
Dog doggy = (Dog)pet2;

但是,当你运行程序时,Java仍然会检查pet2是否真的是一个Dog,如果不是,你会得到一个ClassCastException

(正如biziclop注意到的那样,你标题中的问题与你的意思完全相反。)


好的,明白了。只有一个问题我想问:这样做的目的是什么,比如:Animal pet1 = fifi;? - ipkiss
如果您可以对任何“动物”执行某些操作,则也可以对“fifi”执行该操作。 - Louis Wasserman

2

好的,想象一下两个人:医生和工程师。医生可以治病,工程师可以建造。两者都是人。

现在这里有一个例子。

Person p = new Person(); // somebody that does not know to do anything special.
Doctor d = (Doctor)p; // do you think that casting makes person that does not know anything to be a doctor?

你希望被“选”成医生的人治疗还是想找真正的医生呢?

我相信答案很清楚。 每个医生都是一个人(情况1),但不是每个人都是医生,因为不是每个人都能治疗。对于类层次结构也是如此。子类继承其父类的属性,并可能添加自己的属性。因此,并非父类的任何实例都可以强制转换为子类。


0

案例1是可以的,因为使用(B)部分时,您明确告诉编译器:

即使您只知道a是A类型,我也告诉您它是B类型

因此,对象a被视为B类型

案例2不行,因为编译器无法安全地将b分配给a。如果您写了以下内容,则会被接受

A a = new A();
B b = (B)a;

但在运行时会抛出异常


0
假设情况2也能工作,那么你的编译器会将“b”视为类“B”的对象。现在你可以说“b.fb()”。但实际上,“b”是“A”的一个对象(记得你将类“A”的一个对象分配给了“b”)。类“A”中没有函数fb()。你的应用程序崩溃了!

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