Java 为什么要对对象使用超类引用?
给定以下代码:Java 为什么要对对象使用超类引用?,java,oop,polymorphism,Java,Oop,Polymorphism,给定以下代码: public class Musician { public void play() { // do something } } 我可以使用多态性执行以下操作: Musician m1 = new Guitarist(); Musician m2 = new Drummer(); m1 = m2; 但是,我看不到子类的方法: m1.strummingStrings(); //COMPILATION ERR
public class Musician {
public void play() {
// do something
}
}
我可以使用多态性执行以下操作:
Musician m1 = new Guitarist();
Musician m2 = new Drummer();
m1 = m2;
但是,我看不到子类的方法:
m1.strummingStrings(); //COMPILATION ERROR!
如果我使用:
Guitarist m1 = new Guitarist();
那不是更好吗?使用Musico类型引用子类的对象有什么好处?仅仅是一种可能性,我将能够属性化m1=m2例如,代码>?或者还有其他优势吗
我看到了这篇文章,但我仍然感到困惑:如果您正在编写一个实际需要并使用音乐家的方法,那么您不会意外地想要依赖子类特性
在您的示例中:
m1.strummingStrings(); //COMPILATION ERROR!
如果您正在为接受音乐家的类编写测试驱动程序,那么这是一件好事
当然,在函数的开始和结束之间,编码标准在许多方面并不重要,因为读者和维护人员可以完全理解正在发生的事情。因此,在示例中:
void foo() {
Guitarist g;
g.play();
}
及
很少会有大的不同
但是
及
会有很大的不同。在前一种情况下,客户机代码永远不允许期望吉他手,而在后一种代码中,实现者永远与返回的吉他手耦合,即使将来发现Banjoist可以更容易地实现和返回,由于客户端代码依赖于它。多态性的优势在于,当您可以对任何音乐家调用play()
时,无论它是吉他手、鼓手,还是您可能尚未创建的音乐家的任何其他子类
Musician m1 = new Guitarist();
Musician m2 = new Drummer();
m1.play();
m2.play();
这可能会输出如下内容
Guitarist strumming
Drummer drumming
如果在两个子类中重写play()
方法。这样,调用play()
的代码就不需要知道它实际上是music
的哪个实现,只需要知道它是music
并且保证有play()
方法
多态性的优点不是能够从超类引用调用子类方法,例如strummingStrings
,因为该方法只存在于子类中。它不能保证存在于超类中。如果需要只调用子类方法,如strummingString
,则需要子类引用
您可以在超类music
中定义strummingStrings
,多态性可以工作,但这是一个糟糕的设计。并非所有的音乐家都能在吉他上弹奏弦乐。吉他手和鼓手被称为音乐家的专业。他们表演特定的把戏,他们有特定的玩法。如果你有一个波段
,你可能会有这样的波段:
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();
}
}
}
好问题,但您遇到的问题是您的设计固有的
如果您这样做:
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类通过调用play方法告诉他们何时开始播放。实现细节由子类型决定,继承的功能就是在子类型中实现的 按照您编写类的方式,没有理由将Musitor子类化,因为这两个子类都没有实际使用Musitors play()方法。将m1和m2声明为音乐家而不是它们各自的子类的优点是,实例化类不需要知道play()方法是如何工作的;它可以调用m1.play()和m2.play(),子类知道该做什么
要从这种灵活性中获益,您需要重新构造对象。我建议,至少,将Musitor抽象化,然后将子类中的play()方法重写为strum或drum
如果您想更进一步,可以定义一个Play接口,并为名为StrumGuitar和Drum的类实现该接口,从而封装这些行为。然后,例如,Guitarist的play()方法可以将其行为委托给StrumGuitar类。此外,实例化代码只需调用play(),而不必担心它会做什么
这些技术有助于保持代码的逻辑组织性和灵活性,并允许实例化类在运行时不知道子类的类型,这对代码的灵活性是一个巨大的好处。另一个优点是,如果您希望将所有音乐家放在一个数据结构(如列表)中,您可以这样做,如果没有超类,则无法关联。不确定我是否会称之为重复。你问题的标题不正确。它应该类似于“为什么要对一个对象使用超类引用”,多态性的例子在像这样的孤立片段中往往显得过于成熟和多余。你需要看到一个更大的、设计良好的代码库才能理解这些微妙之处。@反斜杠,我不理解你的意思,因为如果我有一个List List=new ArrayList(),我可以把吉他手或鼓手放进去,即使我已经创建了这两个类的子类。你能指出一本书或一篇文章,在那里可以看到更多这样的技巧吗?
Guitarist foo() {
// ...
}
Musician m1 = new Guitarist();
Musician m2 = new Drummer();
m1.play();
m2.play();
Guitarist strumming
Drummer drumming
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();
}
}
}
public class Drummer extends Musician {
@Override
public void play() {
this.turnsDrumStick();
}
[...]
}
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
}
}