Genaue Definition abstract Klasse und interface?
Durch die Recherche bin ich nun komplett verwirrt. Eine abstrakte Klasse ist eine Oberklasse. Ist der einzige Unterschied zu einer normalen Superklasse, dass die abstrakte Klasse abstrakte Methoden besitzt? Laut den Vorlesungsfolien besitzen abstrakte Klassen abstrakte Methoden, die aber nicht implementiert sind. Die Implementierung findet erst in der ersten konkreten Klasse statt und dort werden alle abstrakten Methoden implementiert. Aber warum? Was ist wenn diese konkrete Klasse diese Methode garnicht braucht? Wozu braucht man überhaupt die abstrakte Klasse und ihre abstrakten Methoden?
Interfaces sind ja komplett abstrakte "Klassen". Was ist der Unterschied zu abstrakten Klassen? Abstrakte Klassen können auch nicht abstrakte Methoden enthalten.. ist das der einzige Unterschied? Eine Klasse kann mehrere Interfaces haben, aber ein Interface kann nur in einer Klasse implementiert werden, stimmt das? Und auch hier werden die Methode des Interfaces in einer normalen Klasse implementiert..
Irgendwie ist mir der Unterschied zwischen abstrakte Klassen und Interfaces nicht klar, genau so warum es diese beiden "Klassen" gibt
2 Antworten
Ist der einzige Unterschied zu einer normalen Superklasse, dass die abstrakte Klasse abstrakte Methoden besitzt?
Um vollständig zu bleiben, nicht nur das. Aufgrund der Tatsache, dass nicht alle Methoden konkret definiert wurden, ist die abstrakte Klasse auch nicht instanzierbar.
Ein Beispiel, um die Problematik zu verdeutlichen:
abstract class Vehicle {
abstract void drive();
}
Vehicle car = new Vehicle();
car.drive();
Angenommen, man würde bereits so weit kommen, die Klasse überhaupt erzeugen zu können: Was sollte das Objekt car nun tun, wenn die drive-Methode aufgerufen wird? Der Referenztyp (Vehicle) weiß zwar, dass es da eine Methode drive gibt, doch das Objekt selbst weiß bei einem Aufruf nicht, was es da nun konkret anstellen soll. Es gibt ja keine konkreten Anweisungen, keinen Code-Block, der ausgeführt werden könnte.
Ein Objekt von einem Typ, in dem nicht eindeutig definiert ist, was bei Methode X, Y oder Z passieren soll, kann nicht angelegt werden. Dies ist soweit der technische Aspekt, einen weiteren erwähne ich im nächsten Abschnitt.
Die Implementierung findet erst in der ersten konkreten Klasse statt und dort werden alle abstrakten Methoden implementiert. Aber warum?
Weil eine abstrakte Klasse, wie es die Bezeichnung schon sagt, abstrakt ist. Etwas, was nicht abstrakt ist, ist nicht konkret / genau definiert.
Um ein Beispiel zu nennen: Du möchtest ein System Zoo beschreiben und hast demzufolge Typen an Tieren, die es zu beschreiben gilt. Für die Klassenhierarchie legst du eine Klasse Tier an, denn diese kann bereits gleiche Eigenschaften und Verhaltensweisen implementieren, die jeder Subtyp wohl haben wird. Da es in der konkreten Welt keine reine Instanz eines Tieres geben kann (Tier ist ja nur ein abstrakter Oberbegriff; es muss aber konkret sein: Löwe, Giraffe, Zebra, ...), wird so eine Klasse als nicht instanzierbar definiert.
abstract class Animal {
public abstract void run();
}
class Turtle extends Animal {
@Override
public void run() {
System.out.println("Moves very slowly.");
}
}
class Emu extends Animal {
@Override
public void run() {
System.out.println("Runs very fast.");
}
}
// main:
ArrayList<Animal> animalsInZoo = new ArrayList<>();
animalsInZoo.add(new Turtle());
animalsInZoo.add(new Emu());
for (Animal animal : animalsInZoo) {
animal.run();
}
So verhält sich die abstrakte Klasse in der Hinsicht wie ein Interface. Sie gibt mittels abstrakter Methoden vor, was jeder konkrete Subtyp definieren muss.
Was ist wenn diese konkrete Klasse diese Methode garnicht braucht?
Dann darf sie entweder nicht von der abstrakten Klasse erben oder die Methode gehört nicht in die Vererbungshierarchie.
Ein Beispiel: Du hast eine abstrakte Klasse Fahrzeug mit einer abstrakten Methode hupen. In diesem Fall wird also die Regel aufgestellt, dass jedes Fahrzeug hupen können muss. Wenn es das nicht kann, ist es innerhalb dieses Systems kein Fahrzeug - oder, wenn man von der grundlegenden Logik ausgeht, die Methode hupen kann nicht als grundsätzliches Verhalten eines Fahrzeugs gewertet werden und muss raus.
Wozu braucht man überhaupt die abstrakte Klasse und ihre abstrakten Methoden?
Sie werden wie gesagt dazu benötigt, einen abstrakten Basistyp zu beschreiben. Gleiche Eigenschaften und Methoden für alle Subtypen werden zum Teil ausgelagert oder zumindest deklariert (abstrakte Methoden).
Logisch gesehen (im Programmdesign) beschreibt eine abstrakte Klasse einen Typ, den es in reellen Welt so nicht als Objekt geben kann. Er gruppiert lediglich mehrere konkrete Subtypen (Stichwort: Polymorphie).
Interfaces sind ja komplett abstrakte "Klassen". Was ist der Unterschied zu abstrakten Klassen?
Abstrakte Klassen können auch konkrete Methoden definieren und die Bestimmung der Sichtbarkeit bei Methodendeklarationen ist freier:
abstract class Human {
public void doSomething() {
this.doSomethingElse();
}
protected abstract void doSomethingElse();
}
class Man extends Human {
@Override
protected void doSomethingElse() {
System.out.println("do something else.");
}
}
// main:
Human paul = new Man();
paul.doSomething();
Während ein reines Interface verlangt, dass jede Methode sichtbar implementiert werden muss, kann es bei einer abstrakten Klasse ebenso abstrakte Methoden mit dem protected access modifier geben. Ein absolutes Muss gibt es für den Subtyp einer abstrakten Klasse auch nicht, jede abstrakte Methode zu definieren. In diesem Fall wird der Subtyp aber ebenso zu einer abstrakten Klasse (und muss auch so definiert werden).
abstract class Human {
public abstract void speak();
}
// doesn't override speak method, so this class needs to be defined as abstract
abstract class Woman extends Human {
}
class Teacher extends Woman {
@Override
public void speak() {
System.out.println("Speaks loud and clear");
}
}
Des Weiteren handelt es sich schlichtweg um eine (fast normale) Klasse, die ihre Elemente vererben kann. Attribute werden dabei nicht automatisch zu statischen Konstanten (wie in Interfaces), es gibt die Möglichkeit, Konstruktoren zu nutzen und es gelten bei der Vererbung die üblichen Regeln (keine Mehrfachvererbung). Zudem kann eine abstrakte Klasse zwischen objektgebundenen oder objektungebundenen (statischen) Elementen unterscheiden.
Insgesamt kann eine abstrakte Klasse mehr als ein Interface, ist aber an anderen Stellen (Vererbung) wieder beschränkt. Hinsichtlich der Performance ist ein Interface natürlich leichtgewichtiger, da es wirklich nur Signaturen und Konstanten beinhaltet.
Eine Klasse kann mehrere Interfaces haben, aber ein Interface kann nur in einer Klasse implementiert werden, stimmt das?
Nein. Ein Interface kann von mehreren Klassen implementiert werden und jede Klasse kann mehrere Interfaces implementieren.
interface IRunnable {
}
interface IEatable {
}
interface IMortal {
}
class Animal implements IRunnable, IEatable, IMortal {
}
class Plant implements IEatable, IMortal {
}
Anders ist es bei Klassen. Jede Klasse kann nur von einer anderen Klasse erben (cyclic inheritance ist nicht erlaubt). Eine Klasse kann von unterschiedlichen anderen Klassen geerbt werden.
class Base {
}
class OtherBase {
}
class Sub extends Base {
}
// error: multiple inheritance isn't allowed
class SomeOtherSub extends Base, OtherBase {
}
// error: cyclic inheritance isn't allowed
class A extends C {
}
class B extends A {
}
class C extends B {
}
Irgendwie ist mir der Unterschied zwischen abstrakte Klassen und Interfaces nicht klar, genau so warum es diese beiden "Klassen" gibt
Den grundlegenden Nutzen von Interfaces habe ich hier bereits einmal erklärt. Zusätzlich kannst du bei Interesse meine Antworten zu folgenden beiden Fragen lesen, da sie mit dem Thema auch etwas im Zusammenhang stehen:
Noch eine Anmerkung zu Begrifflichkeiten, die möglicherweise verwirren könnten: Grundlegend bezeichnet ein Interface stets eine Art Schnittstelle, die bestimmte Funktionalitäten anbietet, wobei deren konkrete Implementation sonstwo liegen kann (das ist im ersten Punkt erst einmal irrelevant). Dabei kann sich der Begriff Interface im Konkreten je nach Kontext auf etwas anderes beziehen:
- Auf das sprachliche Mittel Interface, welches über das Schlüsselwort interface eingeleitet wird
- Auf die sprachlichen Mittel Interface und abstrakte Klasse (als Oberbegriff)
- Auf eine Kommunikationsschnittstelle im Programmdesign (bspw. eine Schnittstelle einer API / Bibliothek, die die Kommunikation mit dieser ermöglicht, da sie verfügbare Funktionalitäten nach außen hin sichtbar macht - in Relation dazu steht das Facade Pattern)
Falls folgend noch Fragen vorherrschen sollten, schreibe einen Kommentar.
1) Nur abstrakte Methoden dürfen ohne Körper aufgeführt werden. Daher findet eine Implementierung nicht abstrakter Methoden wie gewohnt bereits in der Basisklasse statt. Wenn du die Methode nochmals in der Subklasse definierst, überschreibt sie die Methode ihrer Basisklasse und du solltest die Override-Annotation davor schreiben.
abstract class Base {
protected void doSomething() {
// do something ...
}
}
class Sub extends Base {
}
class AnotherSub extends Base {
@Override
protected void doSomething () {
// do something else ...
}
}
2) Du kannst jede Klasse, die im Programm nicht instanziert wird, gefahrlos zu einer abstrakten Klasse umwandeln, nur müssen Subklassen (die instanziert werden) natürlich entsprechend reagieren (indem sie die abstrakten Methoden definieren, falls du welche zur Basisklasse hinzufügst).
So viel zur technischen Seite. Abstrakte Klassen sollten aber natürlich mit mehr Bedacht gesetzt werden. Überlege stets im Vorfeld: Kann es zu dem Typ, den du beschreibst, logisch betrachtet eine Instanz geben oder nicht?
Einfache Beispiele für abstrakte Klassen sind: Tier, Fahrzeug, Mensch oder Pflanze, denn diese Worte dienen in unserer Welt lediglich für abstrakte Oberbegriffe. Konkret würdest du an dieser Stelle z.B. eine Bulldogge, einen LKW, einen Mann oder eine Sumpfdotterblume beschreiben.
Wie du siehst, kann also bereits der Name Aufschluss darüber geben, ob es sich um eine abstrakte Klasse handelt oder nicht. Er muss im Vorfeld nur passend gewählt worden sein.
Will den anderen Antworten ihren Sinn und ihre Richtigkeit hier ja mitnichten absprechen, aber in der Hoffnung, mehrere Erklärungen verhelfen zu einer besseren Erkenntnis, gebe ich hier noch mal meinen Senf dazu:
Abstrakte Klassen repräsentieren Objekte - Interfaces repräsentieren Funktionalitäten.Deswegen benennt man erstere in der Regel auch als allgemeines Nomen (z. B. Auto) und letztere eher nach der Funktionalität (IFahrzeug, ILenkbar).
Abstrakte Klassen können Daten und Verhalten beinhalten - Interfaces nur eine SchnittstelleUm den Begriff der Schnittstelle besser zu verstehen, rate ich gerne zum Bild eines USB-Anschlusses (USB-Schnittstelle): Hier werden nur die Kommunikationskanäle standarisiert, ein Vertrag wird vorgegeben, über den Inhalt ist nichts bekannt.
Klassen können nur von genau einer Oberklasse erben - aber beliebig viele Schnittstellen implementieren.Es erscheint irgendwie logisch, dass ein Ding nicht zweies zugleich sein kann, einmal philosophisch angehaucht - hingegen beliebig vieles zu können vermag. So wäre ein Hybridauto wohl als Auto (Basisklasse) denkbar, aber nicht als Dieselauto oder Elektroauto (die auch Auto-Klassen sind) - weil es dann zwei Basisklassen gäbe. Die Lösung für dieses Problem wären Schnittstellen: Alle drei speziellen Autotypen haben die Basisklasse Auto, dann gibt es noch die Schnittstellen DieselFahrzeug und ElektroFahrzeug, die zum Beispiel das Tanken/Befüllen spezifizieren und von den entsprechenden Klassen implementiert werden.
Konkret also:
abstract class Auto {
public void Fahren();
}
interface IDieselFahrzeug {
public void Tanken(double liter);
}
interface IElektroFahrzeug {
public void Laden(double amperestunden);
}
class DieselAuto : Auto
implements IDieselFahrzeug
{
public void Tanken(double liter);
}
class ElektroAuto : Auto
implements IElektroFahrzeug
{
public void Laden(double amperestunden);
}
class HybridAuto : Auto
implements IDieselFahrzeug, IElektrofahrzeug
{
public void Laden(double amperestunden);
public void Tanken(double liter);
}
Vielleicht hilft dir das ja noch ein wenig weiter :)
Danke für diese ausführliche und verständliche Antwort! Du solltest ein Buch über Java schreiben!
Ich habe nun nur noch kleine Fragen:
Mir fehlen Fachbegriffe, ich hoffe meine Fragen sind trotzdem verständlich.