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()中)