views:

633

answers:

7

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.

+5  A: 

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

rethink what you are trying to do.

Omry
I've edited my post and have added what it is I'm actually trying to do. Any ideas?
Cam
+1  A: 

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.

Brabster
A: 

Because if B extends C, then B might have stuff that isn't in C (like instance variables you initialize in the constructor that are not in new C())

glebm
+1  A: 

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.

Joel
+3  A: 

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.

Jon Skeet
Thanks Jon. After some additional thinking this totally makes sense. Another explanation could be this: We can already cast a derived class to a base class - which means we can cast anything to an Object. However, if we could cast a base class to a derived class, we could effectively cast an Object as anything. If we could do both of those things, any class could be casted to any other class - which clearly does not make sense.Thanks for the help, and the validation in that 'parallel class hiarchies' are a pain to get working :)
Cam
+1  A: 

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){
        ...
    }
}
Maurice Perry
Thanks! "In your exemple, you can cast n into a DerivedNode if you are certain that n is an instance of DerivedNode" was extremely helpful!
Cam
+1  A: 

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