将超类作为参数传递给期望子类的方法

Posted

技术标签:

【中文标题】将超类作为参数传递给期望子类的方法【英文标题】: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 分。

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

【问题讨论】:

【参考方案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()。

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

【讨论】:

以上是关于将超类作为参数传递给期望子类的方法的主要内容,如果未能解决你的问题,请参考以下文章

您可以将子类作为参数传递给具有超类参数的函数吗? C++

PHP:获取传递的变量的类名?

我应该将啥作为参数传递给期望 NSError** 的方法?

将 _bstr_t 对象传递给期望 BSTR 作为参数的函数是不是安全?

无法将子类实例作为参数而不是超类传递

为啥我们将字符串数组作为参数传递给 main() 方法,为啥不传递任何集合类型或包装类型或原始类型?