最初我遇到了一个设计问题,我需要一个超类的五个子类,其中除了两个以外的所有子类都使用相同的通用方法,而另外两个类需要特殊处理。我想避免写五次这个方法;两个特例和三个相同的案例。
所以我让每个类继承SuperClass及其doSomething()方法,并使用自己的doSomeThing方法覆盖SubClassSpecial1和SubClassSpecial2。
这一切都很好,直到我写了一个类似
的方法void fooBar(SuperClass obj) {
obj.doSomething()
}
可以将其称为fooBar( new SubClassSpecial1() );
问题是obj
变量的运行时类现在是其超类的运行时类,因此它将调用超类中定义的方法。当然,我可以在超类中创建一个抽象的doSometing()方法,并使每个子类实现自己的版本,但这会重复三个类中的代码。我想避免这样做......
如果我通过
进行大量分支,我会失去任何增益多态性if(obj.getClass().getName() == "SubClassSpecial1" ) ((SubClassSpecial1) obj).doSomething()K;
else if ...
那么我该怎么做才能让设计更优雅,更不讨人喜欢?
答案 0 :(得分:5)
我对你的这部分问题感到有些困惑:
当然,我想要做的是让每个对象调用它自己的doSomething()版本,但没有意识到为了做到这一点,obj需要被声明为子类方法之一。而现在这是一团糟。
当然声明无关紧要,doSomething()方法将始终根据类的运行时类型进行调用。
所以我认为你要做的事情应该很好,例如这些声明都可以用于传递给foobar方法:
SuperClass sc1 = new SubClassSpecial1();
SubClassSpecial2 sc2 = new SubClassSpecial2();
//etc..
答案 1 :(得分:2)
你所描述的应该可以正常工作。
obj.doSomething()实际上是在调用超类的实现吗?如果是,你没有正确地覆盖它。检查您是否在已覆盖的版本中更改了签名。
答案 2 :(得分:1)
当你有这个:
void fooBar(SuperClass obj) {
obj.doSomething();
}
然后obj
的编译时类型为SuperClass
。这意味着编译器将检查SuperClass
是否有doSomething()
方法
在运行时,您可以替换SuperClass
的子类,这是 Liskov Substitution Principle 。方法foobar()
不知道,也不应该知道obj
的运行时类型是什么,只是它来自SuperClass
,因此可以调用doSomething()
。
至于你的例子:
fooBar( new SubClassSpecial1() );
在这种情况下,您碰巧知道参数的运行时类型是SubClassSpecial1
,它明确地覆盖了doSomething()
。 在所有情况下都会调用正确的方法。
关于重构的一个词。您可能需要考虑重构您的层次结构
您的基类SuperClass
应将doSomething()
定义为抽象。您需要相同doSomething()
实现的三个类应该从具有该特定实现的中间基类继承它。您的两个特殊类应直接从SuperClass
继承,并拥有自己的doSomething()
实现。