简体   繁体   中英

Java; casting base class to derived class

Why can't I cast a base class instance to a derived class?

For example, if I have a class B which extends a class C, why can't I do this?

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

or this?

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

Alright let me be more specific as to what I'm trying to do. Here's what I have:

public class Base(){
    protected BaseNode n;
    public void foo(BaseNode x){
        n.foo(x);
    }
}


public class BaseNode(){
    public void foo(BaseNode x){...}
}

Now I want to create a new set of classes which extend Base and Basenode, like this:

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

So essentially I want to add new functionality to Base and BaseNode by extending them both, and adding a function to both of them. Furthermore, Base and BaseNode should be able to be used on their own.

I'd really like to do this without generics if possible.


Alright so I ended up figuring it out, partly thanks to Maruice Perry's answer.

In my constructor for Base , n is instantiated as a BaseNode . All I had to do was re-instantiate n as a DerivedNode in my derived class in the constructor, and it works perfectly.

because if B extends C, it means B is a C and not C is a B.

rethink what you are trying to do.

The existing answers are fine in terms of an abstract argument, but I'd like to make a more concrete one. Suppose you could do that. Then this code would have to compile and run:

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

Where exactly would the implementation of the read method come from? It's abstract in InputStream . Where would it get the data from? It simply isn't appropriate to treat a bare java.lang.Object as an InputStream . It's much better for the cast to throw an exception.

In my experience it's tricky to get "parallel class hierarchies" like the one you're describing to work. You may find that generics help, but it can get hairy very quickly.

You need to use the instanceof keyword to check the type of object referenced by n and typecast the object and call the bar() method. Checkout Derived.bar() method bellow

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" );
    }
}

You can create a constructor for B that takes C as a parameter. See this post for ideas to do what you're trying to do.

Base classes shouldn't know anything about classes derived from them, otherwise the problems highlighted above will arise. Downcasting is a 'code smell', and downcasting in the base class to a derived class is particularly 'smelly'. Such designs can lead to difficult to resolve circular dependencies too.

If you want a base class to make use of derived class implementations use the Template method pattern ie add a virtual or abstract method in your base class and override and implement it in the derived class. You can then safely call this from the base class.

You can't do that because C does not necessarily implement the behaviours you created when you extended it in B.

So, say C has a method foo() . Then you know that you can call foo() on a B, as B extends C, so you can cast accordingly a treat a B as if it was a C with (C)(new B()) .

However - if B has a method bar() , nothing in the subclass relationship says that you can call bar() on C too. Thus you cannot treat a C as if it were a B, and so you cannot cast.

In your exemple, you can cast n into a DerivedNode if you are certain that n is an instance of DerivedNode, or you can use generics:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM