Java; casting base class to derived class

2019-04-18 22:16发布

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.

8条回答
\"骚年 ilove
2楼-- · 2019-04-18 22:47

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 i.e 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.

查看更多
姐就是有狂的资本
3楼-- · 2019-04-18 22:50

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

rethink what you are trying to do.

查看更多
ら.Afraid
4楼-- · 2019-04-18 22:53

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.

查看更多
手持菜刀,她持情操
5楼-- · 2019-04-18 22:53

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){
        ...
    }
}
查看更多
Fickle 薄情
6楼-- · 2019-04-18 23:00

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

查看更多
女痞
7楼-- · 2019-04-18 23:03

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.

查看更多
登录 后发表回答