何时使用抽象 class 作为类型

When to use abstract class as type

因此,在尝试理解抽象 classes 时,我仍然对一件事感到困惑。你什么时候想要声明其抽象的对象类型class。例如

public abstract class GameObject
{
 public abstract void draw();
 public static void main(String[] args)
 {
 GameObject player = new Player();
 Menu menu = new Menu();
 }

}
public class Player extends GameObject
{
 override 
 public void draw()
 {
 // Something
 }

}
public class Menu extends GameObject
{
 override 
 public void draw()
 {
 // Something
 }
}

通常情况下,我会实例化一个播放器类型的播放器对象。但是,我已经看到 abstract classes 用作新对象的变量类型。你什么时候会选择这样做?谢谢!

每次需要变量作为抽象的实例时,您都会这样做 class,但并不真正关心具体类型是什么(并且不希望其余的假定使用特定 subclass 的代码)。例如:

GameObject[] gameObjects = new GameObject[] {new Menu(), new Player()};
drawAll(gameObjects);

...

private void drawAll(GameObject[] gameObjects) {
    for (GameObject gameObject : gameObjects) {
        gameObject.draw();
    }
}

抽象类型通常用作 return 类型(因为您不希望调用者知道 returned 的具体类型是什么:它可能会在以后更改,或者可能会根据关于配置的参数)。

也常被用作方法的参数类型,使方法可以接受抽象类型的任意子class的参数,并多态使用。

当然,作为 array/collection 类型,能够将多个子 class 的实例存储在一个唯一的集合中。

假设您有一个摘要 Animal class,其中包含 eat()sound() 等方法

您可以创建 class 扩展此 Animal class,比方说 DogCat

如果抽象 class 中有抽象方法(这不是强制性的),您需要在 extends Animal 的每个类型中实现该方法。

如果方法不是抽象的,如果你想让它做一些不同于主要的事情,你仍然可以覆盖它 class。

如果您仍然不完全理解,请尝试观看此视频 https://www.youtube.com/watch?v=TyPNvt6Zg8c

假设您要添加功能以使特定 GameObject 不可见。原始实现如下所示:

void makeInvisible(GameObject object) { ... }

此方法应该适用于任何类型的 GameObject(并且能够使它们全部不可见 - 或者它可以抛出 IllegalArgumentException 如果 "wrong" 类型对象被传递)。

然而你的问题似乎特别关注这种声明:

GameObject gameObject = new Player(); // why on earth would you do this -- you wonder

首先想一想您看到以下内容的频率:

List<String> stringList = new ArrayList<>();

然而,这被推荐为最佳实践,因为其余代码不应该知道所选择的特定 List 实现。您可以稍后将其更改为 LinkedList,除了上面那行以外,不做任何更改。

如果您找不到这样的声明对您的抽象有意义的合理场景,则可能是以下情况之一的症状:

  • 您不愿意使用多态性:也就是说,您想为每个子类型定义特定的逻辑并且从不处理它们 "generically" -- 就像在 makeInvisible 示例中一样以上
  • 你的抽象并没有给你带来太多好处;换句话说,它本身不可用,您可能应该重新考虑您的设计。

在您的特定情况下,该摘要 class 设计得不是特别好。

首先,它使 main() 方法对其所有子 class 可见,并且没有理由希望 main 在 [=20] 的范围内=] 或 Menu.

其次,它更适合作为接口(Drawable,也许),因为它所做的只是为 draw 方法定义签名。此更改将使它的多态功能更加明显,因为您应该能够发现需要将对象视为纯粹 Drawable 的位置,这应该可以回答您的问题。