【问题标题】:Java; casting base class to derived class爪哇;将基类转换为派生类
【发布时间】:2011-02-04 01:36:23
【问题描述】:

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

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

【问题讨论】:

  • 首先,你需要制作BaseNode n;受保护,以便 Derived 可以看到它。
  • 我的意思是没有泛型就没有办法做到这一点吗?就像我说的那样,我希望基类能够按原样使用——添加泛型会使它们使用起来更加麻烦,并删除了应该由 Base 封装的 BaseNode 类的封装。

标签: java casting class


【解决方案1】:

因为如果 B 扩展了 C,则意味着 B 是 C 而不是 C 是 B。

重新考虑你想要做什么。

【讨论】:

  • 我已经编辑了我的帖子并添加了我真正想要做的事情。有什么想法吗?
【解决方案2】:

你不能这样做,因为 C 不一定实现你在 B 中扩展它时创建的行为。

所以,假设 C 有一个方法 foo()。然后您知道您可以在 B 上调用 foo(),因为 B 扩展了 C,因此您可以相应地将 B 视为带有 (C)(new B()) 的 C。

但是 - 如果 B 有一个方法 bar(),则子类关系中没有任何内容表明您也可以在 C 上调用 bar()。因此,您不能将 C 视为 B,因此您不能强制转换。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      您可以为 B 创建一个以 C 作为参数的构造函数。 请参阅 this post 了解如何做您想做的事情。

      【讨论】:

        【解决方案5】:

        就抽象论点而言,现有答案很好,但我想提出一个更具体的答案。假设您可以这样做。然后这段代码必须编译并运行:

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

        read 方法的实现究竟来自哪里?它在InputStream 中是抽象的。它会从哪里获取数据?将裸露的java.lang.Object 视为InputStream 只是不合适。演员抛出异常要好得多。

        根据我的经验,让“并行类层次结构”像你描述的那样工作是很棘手的。您可能会发现泛型有所帮助,但它很快就会变得棘手。

        【讨论】:

        • 谢谢乔恩。经过一些额外的思考,这完全有道理。另一种解释可能是:我们已经可以将派生类转换为基类——这意味着我们可以将任何东西转换为对象。但是,如果我们可以将基类转换为派生类,我们就可以有效地将 Object 转换为任何东西。如果我们可以同时做这两件事,那么任何类都可以转换为任何其他类——这显然是没有意义的。感谢您的帮助,“并行类层次结构”中的验证很难开始工作:)
        【解决方案6】:

        在您的示例中,如果您确定 n 是 DerivedNode 的实例,则可以将 n 转换为 DerivedNode,或者您可以使用泛型:

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

        【讨论】:

        • 谢谢! “在您的示例中,如果您确定 n 是 DerivedNode 的实例,您可以将 n 转换为 DerivedNode”非常有帮助!
        【解决方案7】:

        您需要使用 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" );
            }
        }
        

        【讨论】:

          【解决方案8】:

          基类不应该知道派生自它们的类的任何信息,否则就会出现上面强调的问题。向下转型是一种“代码味道”,在基类中向下转型到派生类尤其“臭”。这样的设计也可能导致难以解决循环依赖。

          如果您希望基类使用派生类实现,请使用模板方法模式,即在您的基类中添加一个虚拟或抽象方法,并在派生类中覆盖和实现它。然后,您可以安全地从基类调用它。

          【讨论】:

            猜你喜欢
            • 2017-01-08
            • 2013-11-17
            • 2021-02-18
            • 2017-07-18
            • 2012-09-15
            • 2011-06-30
            相关资源
            最近更新 更多