【问题标题】:instantiating an abstract class in JAVA?在JAVA中实例化一个抽象类?
【发布时间】:2014-09-08 12:38:19
【问题描述】:

我正在学习JAVA,并在关注JAVA:The Complete Reference by Herbert Shildt这本书。

我学习了 Java 中的抽象类,但不明白这句话背后的原因:

抽象类不能直接用new操作符实例化。

我在 web 和 stackoverflow 上搜索并关注了这些问题:Why can't we instantiate a abstract class in JAVA?Why can't we instantiate an interface or an abstract class in java without an anonymous class method?

在一个答案中,有人写道:

简单地说,在一个好的面向对象程序中,你永远不应该想要 实例化一个抽象类或接口。如果你这样做,设计是 可能是错的。

我不明白为什么不实例化抽象类对于良好的OOP 设计很重要。如果有人能给出一个很好的解释,那么请帮忙。

【问题讨论】:

  • @jhamon 我刚刚写了别人给出的答案。如果你能解释得很好,请写在答案中。
  • @jilt3d 编辑了问题。谢谢。
  • 当你意识到你实际上可以为抽象类编写构造函数时,真正的关键就来了,即使编译器实际上不会让你使用它。
  • @JonK 它会让你从子类的构造函数中使用它。
  • @JonK 你能解释一下吗?请!

标签: java class oop design-patterns abstract-class


【解决方案1】:

如果抽象类完全独立存在,则应该是没有意义的东西

以车辆为例。您能否在描述车辆的同时不描述特定类型的车辆?否 - 因为 Vehicle 只是描述一组相关对象的共同特征和行为的一种方式。或者,您可以将它们视为概念

您的报价:

简单地说,在一个好的面向对象程序中,你永远不应该实例化一个抽象类或接口。如果你这样做了,那么设计可能是错误的。

恰到好处。如果您编写了一个想要完全独立实例化的抽象类,那么它就不是抽象类。如果您发现自己处于这种情况,您可能需要执行另一个抽象级别,以将抽象部分与实际开始将类压缩为具体内容的位分开。您应该对抽象类做的唯一一件事就是扩展它 - 将它变成不那么模糊的东西(或者更具体的东西,如果你愿意的话)。

当然,Java 有时看起来有点矛盾。事实上,为抽象类编写构造函数完全有效

abstract class Vehicle {

    // You might have some common variables defined here

    Vehicle() { }
}

起初这似乎有点愚蠢。当我不允许实例化类时,为什么我要编写一个 constructor,它旨在让您实例化一个对象?如果你不写,编译器甚至会为你创建一个默认构造函数!

答案是你允许实例化一个抽象类——你只是不被允许直接使用new关键字来实例化它。但抽象类最重要的部分是它们被设计为可扩展

当您实例化一个抽象类的子类时,您可以在构造函数中显式或隐式调用super();

public class Car extends Vehicle {
    public Car() {
        super(); // If you don't put this here, the compiler will!
    }
}

仔细想想,这实际上是有道理的 - 你不能拥有自己的车辆,但我停在停车场的车绝对是车辆。一旦我对车辆的概念有了具体的扩展,在这种情况下为Car,那么我可以拥有车辆

这使您能够做的最有用的事情可能是创建通用集合。因为 Vehicle 是所有不同类型 Vehicle 的超类,所以我可以说:

List<Vehicle> vehicles = new ArrayList<>();

或者,如果您不想/不能使用菱形运算符 (&lt;&gt;):

List<Vehicle> vehicles = new ArrayList<Vehicle>();

这允许我将任何类型的车辆放入该集合中

vehicles.add(new Car());
vehicles.add(new Van());
vehicles.add(new Lorry());
vehicles.add(new Motorcycle());
// and so on...

虽然这还有很多其他优点,但在这个答案中无法涵盖。

【讨论】:

    【解决方案2】:

    你不能实例化一个抽象类,因为它通常包含没有实现的抽象方法。为了实例化一个类,必须实现它的所有方法。子类将实现抽象方法,您将实例化这些子类。

    然而,即使你实现了所有的方法,如果你想阻止类的用户实例化它,你有时也会将类定义为抽象的。如果要对一个类进行子类化,并且不包含创建全功能对象所需的所有内容,则可以将其定义为抽象类。

    对于接口,它们只是声明没有实现的方法(Java8 中引入的默认方法除外),没有构造函数或类变量。如果不实现它们的所有方法,就无法实例化它们。

    【讨论】:

    • 除此之外,还有另一个原因:如果使用得当,实例化一个抽象类没有意义。我喜欢将抽象类视为“不能单独存在的东西”,例如车辆。没有只是车辆这样的东西,它们总是其他东西,比如汽车或货车。
    • @JonK 是的,这就是我在第二段最后一句中的意思(尽管您的示例很有帮助)。
    【解决方案3】:

    好吧,让我们举个例子,这样你就可以理解为什么在面向对象的范式中,抽象类不是用于实例化,而是用于扩展:

    假设我们要设计一个 Zoo Application ,我们通过一组信息来呈现每个动物,但每个类型都将由它的类来呈现,如果我们想遵循一个好的实践,那么我们创建一个名为 @ 987654321@ 我们在其中放置了所有动物的公共信息(id、价格、entryDate...)。

    public abstract class Animal {
        private long id;
        private Date entryDate;
        private double price;
        private String name;
        // other common info
    }
    

    这将有助于从结构的角度设计其他类

    public Lion extend Animal {
        private boolean isFromAfrica;
    }
    

    【讨论】:

      【解决方案4】:

      抽象类旨在提供骨架结构,因此其中没有具体实现。

      子类应该填充实现,因此它们可以被实例化。

      【讨论】:

        【解决方案5】:

        你的答案在你给定的陈述中:

        简单地说,在一个好的面向对象程序中,你永远不应该实例化一个抽象类或接口。如果你这样做了,那么设计可能是错误的。

        你永远不应该实例化一个抽象类。因为抽象类是一个不完整的类,它的实例化没有任何意义。你为类定义一个行为,它将扩展你的抽象类。更多的是关于一个人说“这些课程应该看起来像这样,并且他们有共同点,所以请填空!”。

        当您需要一个类来实现以下目的时,抽象类很有用 继承和多态,但是实例化没有意义 类本身,只有它的子类。 它们通常在您使用 想要为一组共享一些子类定义一个模板 通用实现代码,,但您还需要保证 无法创建超类的对象。

        所以不要实例化一个抽象类是很重要的,因为根据正确的设计,抽象类的目的是不同的,你想用就用为一组子类定义一个模板,实例化类本身是没有意义的,

        例如:假设您需要创建 Dog、Cat、Hamster 和 Fish 对象。它们具有类似的属性,例如颜色、大小和腿数以及行为,因此您可以创建一个 Animal 超类。然而,动物是什么颜色的?一个Animal对象有多少条腿?在这种情况下,实例化 Animal 类型的对象没有多大意义,而只实例化它的子类。

        【讨论】:

          【解决方案6】:

          一开始可能会觉得莫名其妙,但你会习惯的

          从技术上讲,它不能被实例化,因为 -

          它不包含方法定义。所以在它的实例化上浪费空间!

          理论上-

          如果你可以实例化它,它就违背了抽象类的全部目的......当它与普通类没有什么不同时,你会经历创建概念的所有麻烦

          【讨论】:

          • “它不包含方法定义”...你在说什么?我了解到我们可以定义一个未声明为“抽象”类的方法……一个抽象类必须至少有一个抽象方法……不是吗?
          • 是的..你读的是绝对正确的......但它有一个未定义的方法;不是它!上帝创造一个只有半颗心的人怎么样!
          【解决方案7】:

          抽象类的唯一目的是扩展。抽象类将包含其所有子类的通用代码,子类将仅实现与其相关的那些部分。

          抽象类对于命令模式非常有用。例如,假设你有一个任务需要执行一组动作,那么你可以定义一个 Command 抽象类:

          public abstract class Command
            public void doCommand() {
              // Actual code here doing stuff...
              runInternal();
              // more code;
            }
          
            public abstract void runInternal();
          }
          

          现在的想法是您可以实现一系列命令,例如:

          public void RedCommand extends Command {
             public void runInternal() {
               // Real code that does red things
             }
          }
          

          ...还有

          public void BlueCommand extends Command {
             public void runInternal() {
               // Real code that does blue things
             }
          }
          

          现在,你可以拥有这样的东西:

          List<Command> list = new ArrayList<>();
          list.add(new RedCommand())
          list.add(new RedCommand())
          list.add(new BlueCommand())
          
          for(Command c : list) {
            c.doCommand();
          }
          

          现在可以很容易地在以后添加更多命令,只需扩展 Command 抽象类并实现 runInternal() 方法。与所有事物一样,这种模式可以/被严重滥用,但在正确的环境中,代码易于/可读。

          【讨论】:

          • 这一切都很好,但并没有真正解释为什么你不能/不应该实例化一个抽象类......
          • 感谢 JonK - 改编了答案。
          【解决方案8】:

          原因是有时几个具体的类(可以实例化的类)都共享共同的功能。通用功能可以通过继承来共享。常用方法放在抽象类中。具体子类可以使用抽象类中定义的那些方法,或者具体子类可以覆盖这些方法以实现特殊行为。

          作为类比,想想这四件事:动物、哺乳动物、人类、大象。有真正的生物叫“人”和“象”,但没有叫“动物”或“哺乳动物”的生物。人类和大象就是这样具体的。动物和哺乳动物是我们为描述不同生物共有的特征而创建的抽象概念。人类和大象是哺乳动物的。哺乳动物是动物的

          在 OO 世界中,我们可以将这四样东西建模为类。 Mammal 类是抽象类 Animal 的扩展类。 Human 和 Elephant 是扩展 Mammal 的具体类。将 Animal 和 Mammal 类声明为 abstract 可以告诉其他人在树林里跑来跑去的“动物”或“哺乳动物”之类的东西是不存在的。动物和哺乳动物只是我们为了描述人类、大象和其他生物的共同点而发明的概念。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-11
            • 2019-08-26
            • 1970-01-01
            • 2021-10-06
            • 1970-01-01
            • 2021-08-02
            相关资源
            最近更新 更多