给出以下代码:
public class Musician {
public void play() {
// do something
}
}
public class Drummer extends Musician {
public void turnsDrumStick() {
// do something
}
}
public class Guitarist extends Musician {
public void strummingStrings() {
// do something
}
}
我可以使用多态来执行以下操作:
Musician m1 = new Guitarist();
Musician m2 = new Drummer();
m1 = m2;
但是,我看不到子类的方法:
m1.strummingStrings(); //COMPILATION ERROR!
如果我使用:
Guitarist m1 = new Guitarist();
不是更好吗?使用Musico类型引用子类的对象有什么好处?例如,我可以将m1 = m2;
归为一种可能性吗?或者还有其他优势吗?
我看过这篇文章,但我仍然感到困惑:Using superclass to initialise a subclass object java
答案 0 :(得分:5)
当您可以在任何 play()
上调用Musician
时,无论是Guitarist
,Drummer
还是任何<{1}},都可以获得多态性的优势您可能尚未创建的Musician
的其他子类。
Musician m1 = new Guitarist();
Musician m2 = new Drummer();
m1.play();
m2.play();
这可能会输出类似
的内容Guitarist strumming
Drummer drumming
如果覆盖两个子类中的play()
方法。这样,调用play()
的代码不需要知道它实际上是Musician
的哪个实现,只是它是Musician
并且保证有play()
方法
多态性不能从超类引用中调用诸如strummingStrings
的子类方法,因为该方法仅存在于子类中。它不能保证存在于超类中。如果需要调用仅类的子类方法,例如strummingString
,则需要子类引用。
你可以在超类strummingStrings
中定义Musician
,并且多态可以工作,但这将是一个糟糕的设计。并非所有的音乐家都可以在吉他上弹奏弦乐。
答案 1 :(得分:2)
很好的问题,但您遇到的问题是您的设计所固有的。
如果您这样做:
public class Musician {
public void play() {
// do something
}
}
.
public class Drummer extends Musician {
public void turnsDrumStick() {
// do something
}
public void play() {
//...perhaps some other things....
turnsDrumStick();
}
}
.
public class Guitarist extends Musician {
public void strummingStrings() {
// do something
}
public void play() {
strummingStrings();
//other things
}
}
所以“Band”类由Musician
s的集合组成。乐队类通过调用play方法告诉他们何时开始播放。实现细节留给子类型,这是实现继承的力量的地方。
答案 2 :(得分:0)
如果您正在编写实际需要并使用Musician
的方法,则不会意外地依赖于子类功能。
在你的例子中:
m1.strummingStrings(); //COMPILATION ERROR!
如果您正在为接受Musician
的课程编写测试驱动程序,那么这是一件好事。
当然,在函数的开头和函数的结尾之间,编码标准在很多方面并不重要,因为读者和维护者可以全面了解正在发生的事情。所以在这个例子中:
void foo() {
Guitarist g;
g.play();
}
和
void foo() {
Musician m;
m.play();
}
很少会有很大的不同。
但是
Musician foo() {
// ...
}
和
Guitarist foo() {
// ...
}
会有很大的不同。在前一种情况下,客户端代码永远不会被期望一个吉他手,而在后一个代码中,实现者永远与永远返回的吉他手耦合,即使将来它发现Banjoist可以更容易实现和返回,因为客户端代码取决于它。
答案 3 :(得分:0)
Guitarist
和Drummer
被称为specializations
的{{1}}。他们执行特定的技巧,他们有特定的游戏方式。如果你有一个Musician
,你可能会有类似的东西:
Band
现在,您需要覆盖public class Band {
private List<Musician> musicians = new ArrayList<Musician>();
public void addMusician(Musician m) {
musicians.add(m);
}
public void play() {
for (Musician m : musicians) {
m.play();
}
}
}
和play
中的Drummer
以提供特殊行为:
Musician
答案 4 :(得分:0)
他们你编写类的方式,没有理由将子类化为子类,因为这两个子类实际上都没有使用音乐家的play()方法。将m1和m2声明为音乐家而不是它们各自的子类的优点是实例化类不需要知道play()方法是如何工作的;它可以调用m1.play()和m2.play(),子类知道该怎么做。
要从这种灵活性中受益,您需要重新构建对象。我建议,至少要制作Musician摘要,然后将子类中的play()方法覆盖为strum或drum。
如果你想更进一步,你可以定义一个Play接口,并为名为StrumGuitar和Drum的类实现该接口,从而封装这些行为。然后,例如,吉他手的play()方法可以将它的行为委托给StrumGuitar类。此外,实例化代码只需调用play()而不必担心它的作用。
这些技术有助于保持代码在逻辑上的组织性,灵活性,并允许在运行时实例化类不知道子类的类型,这可以大大提高代码的灵活性。