声明一种类型的对象与另一种类型的对象有什么好处?

时间:2010-12-15 20:16:22

标签: java oop polymorphism

  

可能重复:
  What does Base b2 = new Child(); signify?

我是Java初学者。我理解类继承的概念,但有一点我不太明白。我正在阅读Java for Dummies,它正在解释多态性。它以此代码为例:

class Player {
    public void move() {...

class BetterPlayer extends Player {
    public void move() {...

public class TicTacToeApp {
    public static void main(String[] args) {
        Player p1 = new Player();
        Player p2 = new BetterPlayer();
        playTheGame(p1, p2);
    }

    public static void playTheGame(Player p1, Player p2) {
        p1.move();
        p2.move();
    }
}

为什么将p2创建为Player对象?这是我的理解:

如果p2是BetterPlayer对象(声明如下:BetterPlayer p2 = new BetterPlayer ......):
-upcasting是自动的,所以它仍然适用于playTheGame方法
任何需要BetterPlayer对象的方法都可以使用-p2

但是因为它是作为Player对象创建的,所以现在任何时候p2都被需要BetterPlayer对象的方法使用,它必须显式地转换为BetterPlayer对象,对吧?这似乎更多的工作没有任何好处,所以我猜这样做必须有一些好处;它是什么?

10 个答案:

答案 0 :(得分:5)

变量p2未创建为Player对象。它是指向BetterPlayer对象的引用。

无论p2是被声明为Player还是BetterPlayer引用,它都可以用作Player引用。

将其声明为播放器的一个优点是,您无法耦合任何仅适用于BetterPlayer的代码,并保持打开将来使用代码用于另一个子类选项的选项 - 例如,BestPlayer。

答案 1 :(得分:4)

任何玩家在逻辑上都可以玩游戏(这就是让玩家成为玩家的原因);所以playTheGame不应该关心Player是一个BetterPlayer。多态性的优势正是playTheGame不会被迫关心它不应该关心的事情。

我们不会通过将BetterPlayer分配给Player变量(p2)来引起真正的问题,因为如果我们知道我们要调用一堆特别需要BetterPlayer的函数,那么我们只是不会那样做。 “医生,当我告诉类型系统忘记实际上很重要的事情时会很痛。”在目前的情况下,它并不重要,所以我们没有丢弃特定类型信息的问题。

但是,即使我们宣称p2是一个BetterPlayer,我们仍然可以将它传递给playTheGame,而无需编写任何演员阵容。调用函数时的upcast是隐式的,在函数内部我们不需要向下转换,因为我们不关心我们是否有一个BetterPlayer - 因为我们只使用基本的Player接口。然后,在很多时候(包括在当前代码中)我们首先不需要变量,所以更重要的是类型是什么。 playTheGame(new Player(), new BetterPlayer())工作正常。

答案 2 :(得分:2)

关键是BetterPlayer可以传递给playTheGame(),就好像它是Player一样。当playTheGame()调用p2.Move()时,它会调用函数BetterPlayer.move(),而不是Player.move()

这意味着您可以提出Player的任何子类,它可以在playTheGame()中正常运行,而无需对playTheGame()进行任何更改。这意味着您可以使用您喜欢的任何Player行为创建您喜欢的任何.move()子类,并且playTheGame()可以正常使用它。

首先,您使用旧代码调用新代码而不更改旧代码,这很重要。

另一方面,您提供了一种方法,可以将Player的任何内容插入到某些预先存在的代码中,从而提供抽象屏障。 playTheGame() Player不仅不需要了解Player,也不必坚持使用{{1}}进行调用。

答案 3 :(得分:2)

该书将p2宣布为播放器仅用于说明目的。它可以被声明为BetterPlayer,程序执行完全相同。他们希望表明可以将BetterPlayer分配给Player类型的变量。

答案 4 :(得分:1)

子类与超类具有IS-A关系。 BetterPlayer IS-A播放器。

关于“向上转发”的第一个问题,你的措辞是有缺陷的。你可以将p2传递给方法,因为它需要一个Player和BetterPlayer IS-A Player。

关于你的第二个问题,是的。

对于你的第三个问题,你又是正确的。您可以将p2定义为BetterPlayer,然后您可以在需要播放器的任何地方使用它,以及任何需要BetterPlayer的地方。

答案 5 :(得分:1)

优点是p1.move()p2.move()调用不同的函数,而playTheGame不必执行类似的操作(伪代码,因为我的Java生锈了):

if (p1 is Player)
   call Player.move(p1)
else if (p1 is BetterPlayer)
   call BetterPlayer.move(p1)

if (p2 is Player)
   call Player.move(p2)
else if (p2 is BetterPlayer)
   call BetterPlayer.move(p2)

答案 6 :(得分:1)

让代码更容易维护是件好事 - 如果你需要StupidPlayer,你只需要改变

Player p2 = new BetterPlayer();

Player p2 = new StupidPlayer();

它已经完成了。无需修改其他方法。

答案 7 :(得分:0)

  

将一种类型的对象声明为另一种类型的对象有什么好处?

在这个例子中,没有任何优势。

多态性的一个更实际的例子是可以包含Players,BetterPlayers和任何其他Player子类的玩家数组(Player[])或列表(List<Player>)。

答案 8 :(得分:0)

一个非常典型的类比是绘制形状的系统。

您可以创建许多不同的形状,并需要将它们放在屏幕上。编写没有多态的代码是很棘手的。如果所有特定的形状(方形,圆形,三角形,粘合茶壶,等等),那么你可以把它们放在一个Shape类型的单个数组中然后你可以说像

foreach Shape s in ArrayOfShapes{
  s.draw();  // They all implement draw so it makes our life easy.
}

答案 9 :(得分:0)

好处是除了子类之外,您还可以利用父类的所有函数/属性。这是一种使代码更加灵活的方式,当您了解创建对象系列的更多信息时,您将开始欣赏所提供的额外灵活性。例如,假设我有一个包含5个方法的父类,我可以创建一个子类或子类,并为该类添加更多功能,让您可以访问另外3个更专业的方法。基本思想是子类应该扩展父类的功能,使您能够重用更多代码并使每个类更短,更有凝聚力。如果你想获得对面向对象编程艺术的欣赏,我建议你看看GRASP Patterns wiki。它有足够简单的代码示例,可以帮助您看到类扩展其他类的价值。