将超类作为参数传递给期望子类的方法
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);
在LegalBall
和IllegalBall
中实现
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()。
如果上述方法看起来不合理,那么使用单一方法来添加两个(非常)不同的类型是不合理的,并且您提出的子类型关系不会是可行的方法。
【讨论】:
以上是关于将超类作为参数传递给期望子类的方法的主要内容,如果未能解决你的问题,请参考以下文章