Java中的接口和抽象类混淆与示例

Car*_*rts 19 java oop abstract-class interface

我无法理解何时使用接口而不是抽象类,反之亦然.此外,我很困惑何时扩展与另一个接口的接口.对于长篇文章感到抱歉,但这非常令人困惑.

创建形状似乎是一个受欢迎的起点.假设我们想要一种模拟2D形状的方法.我们知道每个形状都有一个区域.以下两个实现之间有什么区别:

带接口:

public interface Shape {
    public double area();
}

public class Square implements Shape{
    private int length = 5;
    public Square(){...}

    public double area()
         return length * length;
    }
}
Run Code Online (Sandbox Code Playgroud)

用抽象类:

abstract class Shape {
    abstract public double area();
}

public class Square extends Shape {
    private length = 5;
    public Square(){...}

    public double area(){
        return length * length;
    }
Run Code Online (Sandbox Code Playgroud)

我知道抽象类允许您定义实例变量并允许您提供方法实现,而接口不能执行这些操作.但在这种情况下,似乎这两个实现是相同的.所以使用任何一个都没问题?

但现在说我们要描述不同类型的三角形.我们可以有等腰,锐角和直角三角形.对我来说,在这种情况下使用类继承是有意义的.使用'IS-A'定义:右三角形"IS-A"三角形.三角形"IS-A"形状.此外,抽象类应该定义所有子类中常见的行为和属性,因此这是完美的:

与抽象类

abstract Triangle extends Shape {
    private final int sides = 3;
}
class RightTriangle extends Triangle {
    private int base = 4;
    private int height = 5;

    public RightTriangle(){...}

    public double area() {
        return .5 * base * height
    }
}
Run Code Online (Sandbox Code Playgroud)

我们也可以使用接口来实现这一点,Triangle和Shape是接口.但是,与类继承不同(使用'IS-A'关系来定义什么应该是子类),我不知道如何使用接口.我看到两种方式:

第一种方式:

  public interface Triangle {
      public final int sides = 3;
  }
  public class RightTriangle implements Triangle, Shape {
      private int base = 4;
      private int height = 5;

      public RightTriangle(){}
      public double area(){
          return .5 * height * base;
      }
  }
Run Code Online (Sandbox Code Playgroud)

第二种方式:

public interface Triangle extends Shape {
     public final int sides = 3;
} 
public class RightTriangle implements Triangle {
    ....

    public double area(){
         return .5 * height * base;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我看来,这两种方式都有效.但是什么时候你会用另一种方式呢?使用接口而不是抽象类来表示不同的三角形有什么好处吗?尽管我们对形状的描述进行了复杂化,但使用接口与抽象类仍然相似.

接口的一个关键组件是它可以定义可以在不相关的类之间共享的行为.因此Flyable接口将出现在Airplane和Bird中.因此,在这种情况下,很明显接口方法是首选.

另外,要构建扩展另一个接口的混乱接口:在决定应该是什么接口时,应该何时忽略'IS-A'关系?举个例子:LINK.

为什么'VeryBadVampire'应该是一个类,'Vampire'是一个接口?一个'VeryBadVampire'IS-A'吸血鬼',所以我的理解是'吸血鬼'应该是一个超类(可能是抽象类)."吸血鬼"类可以实施"致命"来保持其致命行为.此外,'吸血鬼'IS-A'怪物',所以'怪物'也应该是一个类."吸血鬼"类也可以实现一个名为"危险"的界面来保持其危险行为.如果我们想创造一个名为'BigRat'的新怪物,它是危险但不致命的,那么我们就可以创建一个'BigRat'类来扩展'Monster'并实现'Dangerous'.

以上是否会实现与使用'Vampire'作为接口(在链接中描述)相同的输出?我看到的唯一区别是使用类继承并保留'IS-A'关系会消除很多混乱.然而,这并没有遵循.这样做有什么好处?

即使你想要一个怪物来分享吸血鬼的行为,人们总是可以重新定义对象的表示方式.如果我们想要一种名为'VeryMildVampire'的新型吸血鬼怪物并且我们想创造一个名为'Chupacabra'的吸血鬼般的怪物,我们可以这样做:

'吸血鬼'课程扩展'怪物'实施'危险','致命','血腥''''''''''
'' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' ''''
''''''

但我们也可以这样做:

'VeryMildVampire'延伸'怪物'实施危险,致命,吸血鬼
'Chupacabra'延伸'怪物'实施危险,吸血鬼

第二种方法是创建一个'Vampiric'界面,这样我们就可以更容易地定义一个相关的怪物,而不是创建一堆定义吸血鬼行为的界面(如第一个例子中所示).但这打破了IS-A的关系.所以我很困惑......

小智 6

记住使用抽象类或接口时的基本概念.

当扩展的类与实现它的类更紧密地耦合时,即当两者都具有父子关系时,使用抽象类.

例如:

       abstract class Dog {}

       class Breed1 extends Dog {}

       class Breed2 extends Dog {}
Run Code Online (Sandbox Code Playgroud)

Breed1并且Breed2都是狗的类型并且具有作为狗的一些共同行为.

然而,当实现类具有可以从类实现的功能时,使用接口.

     interface Animal {
         void eat();
         void noise();
     }

     class Tiger implements Animal {}

     class Dog  implements Animal {}
Run Code Online (Sandbox Code Playgroud)

Tiger并且Dog是两个不同的类别但是吃和发出噪音,这是不同的.所以他们可以使用吃和噪音Animal.


Ian*_*Ian 3

当您想要使一个或多个方法不抽象时,请使用抽象类。

如果您想保持所有抽象,请使用接口。