【问题标题】:Passing superclass as parameter to method expecting sub class将超类作为参数传递给期望子类的方法
【发布时间】:2010-09-28 12:02:13
【问题描述】:

我有一个看起来像这样的对象树

           Ball
          /    \
  LegalBall    IllegalBall

我有两种方法:

class o {
AddBall(LegalBall l)
AddBall(IllegalBall i)
}

在另一堂课中,我想做以下事情:

o.AddBall(myBall);

myBall 是 Ball 类型。 并让它根据子类型调用正确的方法。 显然我不能这样做......这些论点不适用。

有人知道我怎样才能实现我想要的吗?或者如果有一个很好的解决方法

谢谢

编辑:我正在尝试构建的应用程序是板球记分卡类型的东西。因此,根据所投球的类型,其他各种元素都应该改变。

我的初衷是能够从某种形式的 UI 中指定球的类型和得分,然后从 BallFactory 创建一个适当类型的球,然后例如当我向球队得分发送一个无球时,它会添加将值添加到球队得分上,但也将值添加到无球计数器。但是当我将同一个球交给击球手分析来处理它时,击球手总得分应该只有 1 分。

我希望这对我的初衷的解释不会太糟糕。

【问题讨论】:

    标签: java inheritance methods polymorphism overloading


    【解决方案1】:

    您可以使用Visitor pattern

    class Basket {
        void AddBall(LegalBall l) {
            System.out.println("LegalBall added to basket");
        }
    
        void AddBall(IllegalBall i) {
            System.out.println("IllegalBall added to basket");
        }
    }
    
    interface Ball {
        void AddBall(Basket b);
    }
    
    class LegalBall implements Ball {
        void AddBall(Basket b) {
            b.AddBall(this);
        }
    }
    
    class IllegalBall implements Ball {
        void AddBall(Basket b) {
            b.AddBall(this);
        }
    }
    

    或者说得更笼统:

    interface BallVisitor {
        void visit(LegalBall l);
        void visit(IllegalBall i);
    }
    
    interface Ball {
        void accept(BallVisitor v);
    }
    
    class LegalBall implements Ball {
        void accept(BallVisitor v) {
            v.visit(this);
        }
    }
    
    class IllegalBall implements Ball {
        void accept(BallVisitor v) {
            v.visit(this);
        }
    }
    
    class Basket implements BallVisitor {
        void visit(LegalBall l) {
            System.out.println("LegalBall added to basket");
        }
    
        void visit(IllegalBall i) {
            System.out.println("IllegalBall added to basket");
        }
    }
    

    【讨论】:

    • 我需要 AddBall 到的不仅仅是 Basket,还有许多其他对象。我是否需要为要添加球的每种类型实现一个方法?
    • 将“访问”和“接受”方法添加到其他正常的界面会很奇怪。
    【解决方案2】:

    你应该尝试只实现一个方法:

    class o {
    AddBall(Ball b)
    }
    

    并尝试依赖多态性来实现针对不同类的不同行为。当然细节取决于 Ball 层次结构的实现。

    【讨论】:

      【解决方案3】:

      您可能(或可能不)想要(部分)访问者模式。

      Ball添加一个方法:

      public abstract void addTo(o o);
      

      LegalBallIllegalBall 中实现

      public void addTo(o o) {
          o.add(this);
      }
      

      【讨论】:

        【解决方案4】:

        上面已经提到了访问者模式。如果您不能或不想修改 Ball、LegalBall 或 IllegalBall,那么您可以尝试根据球的类型创建一个方法分支。请注意,如果您稍后添加 QuasiLegalBall,此代码将中断。您提到的一般情况很困难,因为 LegalBall 和 IllegalBall 的存在并不能阻止存在不适合您描述的两种类型的 Balls(至少从语言的角度来看)。

        class o {
            public void AddBall(Ball b) 
            { 
                if (b instanceof LegalBall) {AddLegalBall(b); }
                else if (b instanceof IllegalBall) {AddIllegalBall(b); }
                else { /*error, new type of ball created*/ }
            }
            private void AddLegalBall(LegalBall b) { }
            private void AddIllegalBall(IllegalBall b) { }
            }
         }
        

        【讨论】:

          【解决方案5】:

          使用访客模式。不这样做很麻烦,并且是这个问题的主题:Work around Java's static method dispatching without Double Dispatch/Visitor patterns

          但是,您能说明您最初的问题吗?我的意思是,为什么您需要为 Add 方法设置这两个重载?也许你可以用一种完全不同的方式来解决它,不需要像访问者模式那样依赖动态调度?

          【讨论】:

            【解决方案6】:

            我同意访客的使用。

            此外,如果您无权访问 Ball 层次结构(源代码访问),或者只是不想在那里修改任何内容;您可以修改您的客户类并从那里决定。

            当然不好的是你会得到很多 if/elseif 语句。

            您需要添加通用方法 ( add( Ball ) ) 并从那里调用其他方法。这是快速、简单和肮脏的。

            :)

            public class Test {
                public static void main( String [] args ) { 
                    Ball ball = new IllegalBall();
                    Test test = new Test();
                    test.add( ball );
                    test.add( new IllegalBall() );
                    test.add( new LegalBall() );
                }
                private void add( Ball ball ){
                    System.out.println("Generic method: I'll have someone handling this : "  + ball );
                    if( ball instanceof IllegalBall ) {
                        add( ( IllegalBall ) ball );
                    } else if( ball instanceof LegalBall ) {
                        add( ( LegalBall ) ball );
                    }
                }
                private void add( IllegalBall ball ){
                    System.out.println("illega-ball: I won't do anything about it! " + ball );
                }
                private void add( LegalBall ball ) { 
                    System.out.println("legal-ball: Hey this is legal I'll do my best!! " + ball );
                }
            }
            
            class Ball {}
            class IllegalBall extends Ball {}
            class LegalBall extends Ball {}
            

            顺便说一句,如果您没有直接引用,编译器会将其发送到最后 2 次调用中的正确方法。

            如您所见,您只需要添加以下代码:

            private void add( Ball ball ){
                System.out.println("Generic method: I'll have someone handling this : "  + ball );
                if( ball instanceof IllegalBall ) {
                    add( ( IllegalBall ) ball );
                } else if( ball instanceof LegalBall ) {
                    add( ( LegalBall ) ball );
                }
            }
            

            【讨论】:

            • 可怕。这是一个教科书示例,说明“instanceof”和方法重载如何成为糟糕设计的代码气味。你在滥用类型系统。
            【解决方案7】:

            访问者模式和使用回调的类似解决方案,在这种情况下似乎只是试图以这样一种方式弯曲您的代码,以使编译器接受您有缺陷的类层次结构。

            我会保留 Ball 类型,并使该类型的属性合法/非法。您将只有 o.add(Ball),您可以在其中根据某些属性或通过方法检查是否合法/非法,例如 isLegal()。

            如果上述方法看起来不合理,那么使用单一方法来添加两个(非常)不同的类型是不合理的,并且您提出的子类型关系不会是可行的方法。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-04-09
              • 2015-12-26
              • 2011-06-21
              相关资源
              最近更新 更多