2010-04-03 117 views
15

为什么我无法将基类实例转换为派生类?Java;将基类转换为派生类

例如,如果我有一个扩展类C的类B,为什么我不能这样做?

B b=(B)(new C()); 

or this?

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){...} 
} 

现在我想创建一个新的组类延伸基地和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){ 
     ... 
    } 
} 

所以基本上我想新的功能添加到基地和BaseNode通过扩展它们并添加一个函数给它们。此外,Base和BaseNode应该可以独立使用。

如果可能的话,我真的很喜欢不使用泛型。


好吧,所以我最终找出了答案,部分得益于Maruice Perry的回答。

在我的构造函数Base中,n被实例化为BaseNode。我所要做的只是在构造函数的派生类中将n重新实例化为DerivedNode,它完美地工作。

+0

首先,你需要使BaseNode n;受到保护,以便派生可以看到它。 – Joel 2010-04-03 19:30:18

+0

Woops(改变它)。 – Cam 2010-04-03 19:31:23

+0

我的意思是没有任何方式没有泛型这样做?就像我说过的,我希望基类可以按原样使用 - 添加泛型会使它们更加麻烦,并且会删除Base应该由Base封装的BaseNode类的封装。 – Cam 2010-04-03 19:43:23

回答

6

您需要使用的instanceof关键字由ñ检查对象的引用的类型和类型转换对象,并调用栏()方法。结帐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"); 
    } 
} 
19

因为如果B扩展C,这意味着B是一个C而不是C是B.

重新考虑你正在尝试做的。

+0

我编辑了我的帖子,并添加了它实际上正在尝试的内容。有任何想法吗? – Cam 2010-04-03 19:26:06

2

你不能这样做,因为C并不一定实现,当你在B.

扩展它,你创建的行为,所以,说C有一个方法foo()。然后你知道你可以在B上调用foo(),因为B扩展了C,所以你可以相应地处理一个B,就好像它是一个C (C)(new B())

但是,如果B有一个方法bar(),子类关系中没有任何内容说你可以在C上调用bar()。因此你不能把C当作B来对待,所以你不能投。

0

因为如果B extends C,那么B可能有东西,不C(像实例变量你在不在新的C构造函数初始化())

3

可以为B中需要创建一个构造C作为参数。 请参阅this post了解您想要做什么的想法。

16

现有的答案在抽象论证方面没有问题,但我想提出更具体的答案。假设你可以这样做。然后这个代码将不得不编译和运行:

// Hypothetical code 
Object object = new Object(); 
InputStream stream = (InputStream) object; // No exception allowed? 
int firstByte = stream.read(); 

在哪里执行read方法从哪里来?它在InputStream中是抽象的。它从哪里获得数据?它只是不适合作为一个InputStream对待裸java.lang.Object。演员投掷异常要好得多。

根据我的经验,获得像您所描述的工作类似的“并行类层次结构”非常棘手。你可能发现泛型帮助,但它可以很快得到毛。

+0

谢谢乔恩。经过一些额外的思考,这完全有道理。另一种解释可能是这样的:我们已经可以将派生类转换为基类 - 这意味着我们可以将任何东西投射到对象上。但是,如果我们可以将基类转换为派生类,那么我们可以有效地将Object转换为任何东西。如果我们能够做到这两件事,那么任何班级都可以上任何其他班级 - 这显然没有意义。 感谢您的帮助,并在'平行类hipedia'验证是一个痛苦得到工作:) – Cam 2010-04-04 02:47:46

2

在你为例,你可以投N转换为DerivedNode如果你确信n是DerivedNode的实例,也可以使用泛型:

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){ 
     ... 
    } 
} 
+0

谢谢! “在你的例子中,如果你确定n是DerivedNode的一个实例,你可以将n转换成DerivedNode”非常有帮助! – Cam 2010-04-03 20:03:20

2

基类不应该知道由其衍生类的任何信息,否则上面强调会出现的问题。向下转换是一种'代码异味',并且在基类中向下派生成派生类特别“臭”。这样的设计也可能导致难以解决循环依赖性。

如果您希望基类使用派生类实现,请使用Template方法模式,即在基类中添加虚拟或抽象方法,并在派生类中重写并实现它。然后你可以从基类安全地调用它。