Java的; 将基类转换为派生类

为什么我不能将基类实例转换为派生类?

例如,如果我有一个扩展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添加新function。 此外,Base和BaseNode应该可以单独使用。

如果可能的话,我真的很想在没有generics的情况下这样做。


好吧,所以我最终搞清楚了,部分归功于Maruice Perry的回答。

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

您需要使用instanceof关键字来检查由n引用的对象的类型,并对该对象进行类型转换并调用bar()方法。 下面的Checkout 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" ); } } 

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

重新思考你想要做什么。

现有答案在抽象论证方面很好,但我想做一个更具体的答案。 假设你可以那样做。 然后这段代码必须编译并运行:

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

read方法的实现究竟来自哪里? 它在InputStream是抽象的。 从哪里获取数据? 将一个简单的java.lang.Object视为InputStream 是不合适的。 对于演员而言抛出exception要好得多。

根据我的经验,获得“并行类层次结构”(如您所描述的工作方式)是很棘手的。 您可能会发现仿制药有所帮助,但它可以很快变得毛茸茸。

您可以为B创建一个构造函数,它将C作为参数。 请参阅此post ,了解您正在尝试做的事情。

你不能这样做,因为C不一定实现你在B中扩展它时创建的行为。

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

但是 – 如果B有方法bar() ,子类关系中没有任何内容表明你也可以在C上调用bar() 。 因此,您不能将C视为B,因此您无法投射。

在您的示例中,如果您确定n是DerivedNode的实例,则可以将n转换为DerivedNode,或者您可以使用generics:

 public class Base { protected N n; public void foo(BaseNode x){ n.foo(x); } } public class BaseNode { public void foo(BaseNode x){...} } public class Derived extends Base { public void bar(DerivedNode x){ n.bar(x); // no problem here - n DOES have bar } } public class DerivedNode extends BaseNode { public void bar(BaseNode){ ... } } 

基类不应该知道从它们派生的类的任何内容,否则将出现上面突出显示的问题。 向下转换是一种“代码味道”,并且在基类中向衍生类的向下转换特别“臭”。 这样的设计也可能导致难以解决循环依赖性。

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

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