何时使用抽象 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,比方说 Dog
和 Cat
。
如果抽象 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
的位置,这应该可以回答您的问题。
因此,在尝试理解抽象 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,比方说 Dog
和 Cat
。
如果抽象 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
的位置,这应该可以回答您的问题。