为什么我不能将基类实例转换为派生类?
例如,如果我有一个扩展C类的B类,为什么我不能这样做呢?
B b=(B)(new C());
还是这个?
C c=new C();
B b=(B)c;
好吧,让我更具体地说明我要做的事情。这就是我所拥有的:
public class Base(){
protected BaseNode n;
public void foo(BaseNode x){
n.foo(x);
}
}
public class BaseNode(){
public void foo(BaseNode x){...}
}
现在我想创建一组扩展Base和Basenode的新类,如下所示:
public class Derived extends Base(){
public void bar(DerivedNode x){
n.bar(x);//problem is here - n doesn't have bar
}
}
public class DerivedNode extends BaseNode(){
public void bar(BaseNode){
...
}
}
所以基本上我想通过扩展它们并向它们添加一个函数来向Base和BaseNode添加新功能。此外,Base和BaseNode应该可以单独使用。
如果可能,我真的很想在没有泛型的情况下这样做。
好吧,所以我最终搞清楚了,部分归功于Maruice Perry的回答。
在Base
的构造函数中,n
被实例化为BaseNode
。我所要做的就是在构造函数的派生类中将n
重新实例化为DerivedNode
,并且它完美地运行。
答案 0 :(得分:21)
因为如果B扩展C,则意味着B是C而不是C是B。
重新考虑你想要做的事情。
答案 1 :(得分:18)
现有答案在抽象论证方面很好,但我想做一个更具体的答案。假设您可以这样做。然后这段代码必须编译并运行:
// Hypothetical code
Object object = new Object();
InputStream stream = (InputStream) object; // No exception allowed?
int firstByte = stream.read();
read
方法的实现究竟来自哪里?它在InputStream
中是抽象的。从哪里获取数据? 不适合将裸java.lang.Object
视为InputStream
。演员抛出异常会好得多。
根据我的经验,获得“并行类层次结构”是很棘手的,就像你描述的那样。你可能发现泛型有帮助,但它可以很快变得毛茸茸。
答案 2 :(得分:6)
您需要使用 instanceof 关键字来检查 n 引用的对象类型并对其进行类型转换对象并调用 bar() 方法。结帐 Derived.bar() 方法
public class Test{
public static void main(String[] args){
DerivedNode dn = new DerivedNode();
Derived d = new Derived(dn);
d.bar( dn );
}
}
class Base{
protected BaseNode n;
public Base(BaseNode _n){
this.n = _n;
}
public void foo(BaseNode x){
n.foo(x);
}
}
class BaseNode{
public void foo(BaseNode x){
System.out.println( "BaseNode foo" );
}
}
class Derived extends Base{
public Derived(BaseNode n){
super(n);
}
public void bar(DerivedNode x){
if( n instanceof DerivedNode ){
// Type cast to DerivedNode to access bar
((DerivedNode)n).bar(x);
}
else {
// Throw exception or what ever
throw new RuntimeException("Invalid Object Type");
}
}
}
class DerivedNode extends BaseNode{
public void bar(BaseNode b){
System.out.println( "DerivedNode bar" );
}
}
答案 3 :(得分:3)
您可以为B创建一个构造函数,它将C作为参数。 请参阅this post了解您要执行的操作的想法。
答案 4 :(得分:2)
您不能这样做,因为C不一定实现您在B中扩展它时创建的行为。
所以,说C有一个方法foo()
。然后你知道你可以在B上调用foo()
,因为B扩展了C,所以你可以相应地对待一个B,就像它是一个带(C)(new B())
的C一样。
但是 - 如果B有方法bar()
,则子类关系中没有任何内容表明您也可以在C上调用bar()
。因此,你不能将C视为B,因此你无法施放。
答案 5 :(得分:2)
在您的示例中,如果您确定n是派生节点的实例,则可以将n强制转换为派生节点,或者您可以使用泛型:
public class Base<N extends BaseNode> {
protected N n;
public void foo(BaseNode x){
n.foo(x);
}
}
public class BaseNode {
public void foo(BaseNode x){...}
}
public class Derived extends Base<DerivedNode> {
public void bar(DerivedNode x){
n.bar(x); // no problem here - n DOES have bar
}
}
public class DerivedNode extends BaseNode {
public void bar(BaseNode){
...
}
}
答案 6 :(得分:2)
基类不应该知道从它们派生的类的任何信息,否则将出现上面突出显示的问题。向下转换是一种“代码味道”,并且在基类中向衍生类的向下转换特别“臭”。这样的设计也可能导致难以解决循环依赖性。
如果希望基类使用派生类实现,请使用Template方法模式,即在基类中添加虚拟或抽象方法,并在派生类中重写并实现它。然后,您可以从基类安全地调用它。
答案 7 :(得分:0)
因为如果B extends C
,那么B可能有不在C中的东西(比如你在构造函数中初始化的实例变量不在新的C()中)