Interfaces und Abstract-Klassen verwechseln Java mit Beispielen

Ich habe Probleme zu verstehen, wann ich eine Schnittstelle anstelle einer abstrakten Klasse verwenden soll und umgekehrt. Außerdem bin ich verwirrt, wenn eine Schnittstelle mit einer anderen Schnittstelle erweitert werden soll. Entschuldigung für den langen Beitrag, aber das ist sehr verwirrend.

Das Erstellen von Formen scheint ein beliebter Ausgangspunkt zu sein. Angenommen, wir möchten 2D-Formen modellieren. Wir wissen, dass jede Form eine Fläche hat. Was wäre der Unterschied zwischen den folgenden beiden Implementierungen:

mit Schnittstellen:

public interface Shape {
    public double area();
}

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

    public double area()
         return length * length;
    }
}

mit abstrakter Klasse:

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

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

    public double area(){
        return length * length;
    }

Ich verstehe, dass abstrakte Klassen es Ihnen ermöglichen, Instanzvariablen zu definieren und Methodenimplementierungen anzugeben, während eine Schnittstelle diese Dinge nicht ausführen kann. In diesem Fall scheinen diese beiden Implementierungen identisch zu sein. Also ist es in Ordnung, jemanden zu benutzen?

Aber jetzt sagen wir, wir wollen verschiedene Arten von Dreiecken beschreiben. Wir können gleichschenklige, spitze und rechtwinklige Dreiecke haben. Für mich ist es in diesem Fall sinnvoll, die Klassenvererbung zu verwenden. Verwendung der 'IS-A'-Definition: Ein rechtwinkliges "IS-A" -Dreieck. Ein Dreieck "IS-A" -Form. Außerdem sollte eine abstrakte Klasse Verhalten und Attribute definieren, die in allen Unterklassen gleich sind. Dies ist also perfekt:

mit abstrakter Klasse

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
    }
}

Wir können dies auch mit Interfaces tun, wobei Triangle und Shape Interfaces sind. Im Gegensatz zur Klassenvererbung (mithilfe der 'IS-A'-Beziehung, um zu definieren, was eine Unterklasse sein soll) bin ich mir jedoch nicht sicher, wie eine Schnittstelle verwendet werden soll. Ich sehe zwei Möglichkeiten:

Erster Weg:

  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;
      }
  }

Zweiter Weg:

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

    public double area(){
         return .5 * height * base;
    }
}

Mir scheint, dass beide Wege funktionieren. Aber wann würden Sie einen Weg über den anderen wählen? Und hat die Verwendung von Interfaces Vorteile gegenüber abstrakten Klassen, um verschiedene Dreiecke darzustellen? Auch wenn wir die Beschreibung einer Form kompliziert haben, scheint die Verwendung von Interface und abstrakter Klasse immer noch äquivalent zu sein.

Eine wichtige Komponente für Schnittstellen ist, dass sie Verhalten definieren kann, die von nicht verbundenen Klassen gemeinsam genutzt werden können. Eine Schnittstelle Flyable wäre also sowohl in Flugzeugklassen als auch in Bird vorhanden. In diesem Fall ist es klar, dass ein Schnittstellenansatz bevorzugt wird.

Aufbauend auf der verwirrenden Schnittstelle, die eine andere Schnittstelle erweitert: Wann sollte die 'IS-A'-Beziehung ignoriert werden, wenn entschieden wird, was eine Schnittstelle sein soll? Nehmen Sie dieses Beispiel:VERKNÜPFUNG.

Warum sollte 'VeryBadVampire' eine Klasse und 'Vampire' eine Schnittstelle sein? Ein 'VeryBadVampire' IST ein 'Vampire', daher sollte ein 'Vampire' meines Erachtens eine Superklasse sein (vielleicht eine abstrakte Klasse). Eine 'Vampir'-Klasse kann' Tödlich 'implementieren, um ihr tödliches Verhalten beizubehalten. Außerdem ist ein "Vampir" ein "Monster", daher sollte "Monster" auch eine Klasse sein. Eine 'Vampire'-Klasse kann auch eine Schnittstelle namens' Dangerous 'implementieren, um ihr gefährliches Verhalten beizubehalten. Wenn wir ein neues Monster namens "BigRat" erstellen möchten, das gefährlich, aber nicht tödlich ist, können wir eine "BigRat" -Klasse erstellen, die "Monster" erweitert und "Gefährlich" implementiert.

Würden die oben genannten Funktionen nicht die gleiche Ausgabe erzielen wie die Verwendung von "Vampire" als Schnittstelle (siehe Link)? Der einzige Unterschied, den ich sehe, ist, dass die Verwendung der Klassenvererbung und die Beibehaltung der 'IS-A'-Beziehung viel Verwirrung stiftet. Dies wird jedoch nicht befolgt. Was ist der Vorteil davon?

Selbst wenn Sie möchten, dass ein Monster das Verhalten eines Vampirs teilt, können Sie jederzeit neu definieren, wie die Objekte dargestellt werden. Wenn wir eine neue Art von Vampir-Monster namens "VeryMildVampire" und ein vampirähnliches Monster namens "Chupacabra" wollten, können wir dies tun:

"Vampire" -Klasse erweitert "Monster" -Instrumente "Dangerous", "Lethal", "BloodSuckable"
Die Klasse 'VeryMildVampire' erweitert die Klasse 'Vampire'
"Chupacabra" -Klasse erweitert "Monster" implementiert "BloodSuckable"

Das können wir aber auch:

'VeryMildVampire' erweitert die 'Monster'-Werkzeuge Dangerous, Lethal, Vampiric
'Chupacabra' erweitert 'Monster' implementiert Gefährlich, Vampirisch

Der zweite Weg hierher erzeugt eine "Vampir" -Schnittstelle, so dass wir leichter ein verwandtes Monster definieren können als eine Reihe von Schnittstellen, die das Verhalten von Vampiren definieren (wie im ersten Beispiel). Dies unterbricht jedoch die IS-A-Beziehung. Also bin ich verwirrt ...

Antworten auf die Frage(7)

Ihre Antwort auf die Frage