【问题标题】:Why do we need abstract methods?为什么我们需要抽象方法?
【发布时间】:2020-04-03 21:09:10
【问题描述】:

我最近一直在研究抽象方法,我不明白为什么我们需要它们? 我的意思是,毕竟,我们只是在覆盖它们。你知道它只是一个声明吗?为什么我们需要它们?

另外,我尝试从互联网上理解这一点,到处都有解释,比如想象有一个抽象类人类,然后有它的子类 disablednot disabled 然后人类类walking() 中的抽象函数将包含不同的主体或代码。现在我要说的是为什么我们不在禁用而不是禁用的子类中创建一个函数而不是覆盖。因此再次回到第一段中的问题。请解释一下。

【问题讨论】:

  • 你不能调用也不能使用没有声明的东西。
  • 我说为什么要在抽象类中声明它而不是在子类中创建一个函数?抽象方法有什么需要(一定有,我想知道)?
  • abstract 成员要求子类覆盖它们。没有abstract,以后写子类的时候可能会忘记覆盖它。

标签: java inheritance multiple-inheritance


【解决方案1】:

基类型中的抽象方法定义是保证该类型的每个具体实现都将具有该方法的实现的契约。

没有它,编译器将不允许您在基类型的引用上调用该方法,因为它不能保证这样的方法将永远存在。

如果你有

MyBaseClass x = getAnInstance();
x.doTheThing();

MyBaseClass 没有有一个doTheThing 方法,那么编译器会告诉你它不能让你这样做。通过添加 abstract doTheThing 方法,您可以保证 getAnInstance() 可以返回的每个具体实现都有一个实现,这对于编译器来说已经足够好了,所以它会让您调用该方法。

基本上一个更基本的事实,首先需要了解的是:

您会遇到变量的类型比它所持有的值的类型更通用的实例。在简单的情况下,您可以将变量设为特定类型:

MyDerivedClassA a = new MyDerivcedClassA();

在这种情况下,您显然可以调用MyDerivedClassA 的任何方法,并且不需要基类中的任何抽象方法。

但有时你想用 any MyBaseClass 实例做某事而你不知道它是什么具体类型:

public void doTheThingsForAll(Collection<? extends MyBaseClass> baseClassReferences) {
  for (MyBaseClass myBaseReference : baseClassReferences) {
    myBaseReference.doTheThing();
  }
}

如果您的 MyBaseClass 没有 doTheThing 抽象方法,那么编译器不会让您这样做。

【讨论】:

    【解决方案2】:

    继续你的例子,在某些时候你可能有一个人类列表,你并不关心他们是否被禁用,你关心的是你想调用 walk() 方法他们。为此,Human 类需要定义一个 walk() 方法。但是,您可能不知道如何在不知道人类是否残疾的情况下实现它。因此,您将实现留给继承类。

    【讨论】:

      【解决方案3】:

      抽象方法最明显的用途之一是让抽象类从其他方法的实现中调用它们。

      这是一个例子:

      class AbstractToy {
          protected abstract String getName();
          protected abstract String getSize();
          public String getDescription() {
              return "This is a really "+getSize()+" "+getName();
          }
      }
      class ToyBear extends AbstractToy {
          protected override String getName() { return "bear"; }
          protected override String getSize() { return "big"; }
      }
      class ToyPenguin extends AbstractToy {
          protected override String getName() { return "penguin"; }
          protected override String getSize() { return "tiny"; }
      }
      

      注意AbstractToygetDescription 实现如何能够调用getNamegetSize,即使定义在子类中。这是称为Template Method 的著名设计模式的一个实例。

      【讨论】:

        【解决方案4】:

        在其他答案中有一些如何你会使用它的例子,所以让我解释一下为什么你可能会这样做。

        首先,面向对象设计的一个共同规则是,一般而言,您应该尝试针对接口而不是特定实现进行编程。如果您以后需要更改某些行为,这往往会提高程序的灵活性和可维护性。例如,在我编写的一个程序中,我们将数据写入 CSV 文件。后来我们决定改用写入 Excel 文件。对接口进行编程(而不是特定实现)使我们更容易做出这种改变,因为我们可以“插入”一个新的类来写入 Excel 文件,而不是写入 CSV 文件的类。

        你可能还没有研究过这个,但这对某些design patterns 来说实际上很重要。 Factory PatternStrategy PatternState Pattern 是其中可能有帮助的几个值得注意的例子。

        就上下文而言,设计模式是描述和记录已知问题解决方案的标准方式。例如,如果有人说“你应该使用策略模式来解决这个问题”,这说明了你应该如何解决问题的大致思路。

        【讨论】:

          【解决方案5】:

          因为有时我们需要一个在其实例中表现不同的方法。

          例如,假设一个类 Animal 包含一个方法 Shout。 我们将拥有这个Animal 类的不同实例,但在某些情况下我们需要以不同的方式实现该方法,如下所示:

          class Animal:
              /**
                 different properties and methods
                 which are shared between all animals here
              */
              ...
          
              method shout():
                  pass
          
          class Dog extends Animal:
              method shout():
                  makeDogVoice()
          
          class Cat extends Animal:
              method shout():
                  makeCatVoice()
          
          
          dog = new Animal
          cat = new Animal
          
          dog.shout()
          cat.shout()
          

          所以狗像狗一样叫,猫像猫一样叫!没有两次实施共享行为

          在这些情况下,大喊大叫的行为有所不同。所以我们需要抽象类。

          【讨论】:

            【解决方案6】:

            假设您不了解实现,但仍想声明一个方法,那么我们可以借助抽象修饰符将其设为抽象方法。对于抽象方法,只有声明可用,但没有实现。因此它们应该以; 结尾

            示例:

            public abstract void m1(); // this is correct 
            public abstract void m1(){  ...  } // this is wrong
            

            优点:通过在父类中声明抽象方法,我们可以为子类提供指导,以便强制实现哪些方法。

            例子:

            abstract class Vehicle{
              abstract int getNoOfWheels();
            }
            
            Class Bus extends Car{
              public int getNoOfWheels(){
                 return 4;
              }
            }
            

            【讨论】:

              【解决方案7】:

              如果您想要简短的答案,请考虑一下:

              你有一个抽象类Car

              您实现了 2 个扩展它的类,FerrariMercedes

              现在: 如果您对所有汽车通用的方法 drive() 执行以下操作之一会怎样:

              1) 改变了方法的可见性,

              2) 将方法名称从driving更改为Driving,

              3) 将返回类型从布尔值更改为 int

              考虑一下。它似乎没有任何区别,因为它们是不同的实现?

              错了!

              如果我要遍历汽车数组,我将不得不为每种类型的汽车调用不同的方法,从而使抽象的这种实现毫无用处。

              抽象类用于使用通用模板对类进行分组,这些模板共享通用属性。这有帮助的一种方法是在数组上循环: 抽象方法确保所有汽车都声明相同的方法, 因此,Car 的子类的任何对象都将具有抽象类中定义的方法 drive(),从而使上述 for 循环易于实现。

              希望这会有所帮助。

              【讨论】:

                猜你喜欢
                • 2012-12-20
                • 2015-06-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-03-18
                • 1970-01-01
                • 2020-04-13
                • 1970-01-01
                相关资源
                最近更新 更多