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

时间:2010-04-03 19:13:14

标签: java casting class

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

例如,如果我有一个扩展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,并且它完美地运行。

8 个答案:

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