CodeGym /Java-Blog /Germany /Abstrakte Klassen in Java
Autor
Vasyl Malik
Senior Java Developer at CodeGym

Abstrakte Klassen in Java

Veröffentlicht in der Gruppe Germany
Hallo! In früheren Lektionen haben wir Interfaces kennengelernt und herausgefunden, wofür sie da sind. Das heutige Thema wird daran anknüpfen. Wir wollen uns abstrakte Klassen in Java ansehen.

Darum werden Klassen „abstrakt“ genannt

Du erinnerst dich wahrscheinlich noch daran, was „Abstraktion“ ist – wir sind es bereits durchgegangen. :) Doch keine Angst, falls du es vergessen haben solltest. Denk einfach daran: Es ist ein Prinzip der OOP, das besagt, dass wir beim Entwerfen von Klassen und Erstellen von Objekten nur die wesentlichen Eigenschaften einer Entität identifizieren und das Irrelevante außen vor lassen sollten. Wenn wir zum Beispiel eine SchoolTeacher-Klasse entwerfen, brauchen wir wohl kaum eine Eigenschaft „groesse“. Für einen Lehrer ist diese Eigenschaft tatsächlich irrelevant. Aber wenn wir eine BasketballPlayer-Klasse erstellen, dann wäre die Größe ein wichtiges Merkmal. Also pass gut auf. Eine abstrakte Klasse ist ein unvollendeter Rohling für eine Gruppe zukünftiger Klassen. Der Rohling kann nicht unverändert verwendet werden. Die Klasse ist eben ein „Rohling“. Aber sie beschreibt bestimmte Zustände und allgemeine Verhaltensweisen, die zukünftige Klassen, die die abstrakte Klasse erben, besitzen werden.Abstrakte Klassen in Java - 1

Beispiele für abstrakte Java-Klassen

Sehen wir uns ein einfaches Beispiel mit Autos an:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public abstract void gas();

   public abstract void brake();

   public String getModel() {
       return model;
   }

   public void setModel(String model) {
       this.model = model;
   }

   public String getColor() {
       return color;
   }

   public void setColor(String color) {
       this.color = color;
   }

   public int getMaxSpeed() {
       return maxSpeed;
   }

   public void setMaxSpeed(int maxSpeed) {
       this.maxSpeed = maxSpeed;
   }
}
So sieht eine sehr einfache abstrakte Klasse aus. Du siehst: Nichts besonderes :) Warum brauchen wir sowas? Zunächst einmal beschreibt sie unsere erforderliche Entität, ein Auto, auf möglichst abstrakte Weise. Es gibt einen Grund, warum wir das Wort abstract verwenden. In der realen Welt gibt es keine „abstrakten Autos“. Es gibt Lastwagen, Rennwagen, Limousinen, Coupés und SUVs. Unsere abstrakte Klasse ist einfach eine „Blaupause“, die wir später zur Erstellung von Autoklassen verwenden werden.

public class Sedan extends Car {

   @Override
   public void gas() {
       System.out.println("The sedan is accelerating!");
   }

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }

}
Das ist dem, worüber wir in den Lektionen über Vererbung gesprochen haben, sehr ähnlich. Aber in diesen Lektionen hatten wir einen Auto-Klasse (Car), und ihre Methoden waren nicht abstrakt. Aber diese Lösung hat einige Nachteile, die in abstrakten Klassen beseitigt werden. Zuallererst kannst du keine Instanz einer abstrakten Klasse erstellen:

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
Die Erfinder von Java haben dieses „Feature“ absichtlich eingebaut. Noch einmal zur Erinnerung: Eine abstrakte Klasse ist nur eine Blaupause für zukünftige „normale“ Klassen. Wir brauchen keine Exemplare einer Blaupause, richtig? Und wir erzeugen keine Instanzen einer abstrakten Klasse :) Aber wenn die Klasse Car nicht abstrakt wäre, könnten wir problemlos Instanzen davon erstellen:

public class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public void gas() {
       // Some logic
   }

    public void brake() {
       // Some logic
   }
}


public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Everything is fine. A car is created.
   }
}
Jetzt hat unser Programm eine Art unverständliches Auto – es ist kein Lastwagen, kein Rennwagen, keine Limousine, und es ist völlig unklar, was es ist. Das ist genau das „abstrakte Auto“, das es im wirklichen Leben eigentlich gar nicht gibt. Wir können uns das gleiche Beispiel anhand von Tieren ansehen. Stell dir eine Animal-Klasse vor(abstrakte Tiere). Es ist unklar, um welche Art von Tier es sich handelt, zu welcher Gattung es gehört und welche Eigenschaften es hat. Es wäre seltsam, das in einem Programm zu verwenden. In der Natur gibt es keine „abstrakten Tiere“. Nur Hunde, Katzen, Füchse, Maulwürfe usw. Abstrakte Klassen liefern uns die Blaupausen für „normale“ Klassen. Sie geben uns einen Grundzustand und ein Basisverhalten. Zum Beispiel sollten alle Autos ein Modell, eine Farbe und eine Höchstgeschwindigkeit haben, und man sollte in der Lage sein, Gas zu geben und zu bremsen. Das war‘s. Das ist ein allgemeiner abstrakter Plan. Als nächstes entwirfst du die Klassen, die du brauchst. Anmerkung: Zwei Methoden in der abstrakten Klasse sind ebenfalls als abstract gekennzeichnet, und sie haben keine Implementierung. Der Grund ist derselbe: Abstrakte Klassen erstellen kein Standardverhalten für abstrakte Autos. Sie geben nur an, was jedes Auto können muss. Wenn du jedoch ein Standardverhalten benötigst, kannst du Methoden in einer abstrakten Klasse implementieren. Java verbietet dies nicht:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public void gas() {
       System.out.println("Gas!");
   }

   public abstract void brake();

   // Getters and setters
}


public class Sedan extends Car {

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }

}

public class Main {

   public static void main(String[] args) {

       Sedan sedan = new Sedan();
       sedan.gas();
   }
}
Konsolenausgabe:
Gas!
Wie du siehst, haben wir die erste Methode in der abstrakten Klasse implementiert, aber nicht die zweite. Infolgedessen ist das Verhalten unserer Sedan-Klasse in zwei Teile geteilt: Wenn du die gas()-Methode aufrufst, „steigt“ der Aufruf bis zur abstrakten Elternklasse Car nach oben, aber wir haben die brake()-Methode in der Sedan-Klasse überschrieben. Das ist sehr praktisch und flexibel. Aber jetzt ist unsere Klasse nicht mehr so abstrakt? Immerhin ist die Hälfte ihrer Methoden implementiert. Dies ist tatsächlich ein sehr wichtiges Merkmal – eine Klasse ist dann abstrakt, wenn mindestens eine ihrer Methoden abstrakt ist. Eine von zwei Methoden, oder sogar nur eine von tausend Methoden – das macht keinen Unterschied. Wir können sogar alle Methoden implementieren und keine von ihnen abstrakt lassen. Dann wäre es eine abstrakte Klasse ohne abstrakte Methoden. Im Prinzip ist dies möglich, und der Compiler wird keine Fehler erzeugen, aber wir sollten das besser nicht tun: Das Wort abstract verliert seine Bedeutung, und Ihre Programmiererkollegen werden sehr überrascht sein :/ Gleichzeitig muss jede Kindklasse, wenn eine Methode mit dem Wort „abstract“ markiert ist, diese implementieren oder als abstrakt deklarieren. Andernfalls wird der Compiler einen Fehler erzeugen. Natürlich kann jede Klasse nur eine abstrakte Klasse erben, daher gibt es in Bezug auf die Vererbung keinen Unterschied zwischen abstrakten und gewöhnlichen Klassen. Es spielt keine Rolle, ob wir eine abstrakte oder eine gewöhnliche Klasse erben, es kann nur eine Elternklasse geben.

Warum gibt es in Java keine Mehrfachvererbung von Klassen?

Wir haben bereits gesagt, dass Java keine Mehrfachvererbung bietet, aber warum das so ist, haben wir noch nicht untersucht. Das wollen wir jetzt tun. Wenn Java Mehrfachvererbung unterstützen würde, wären die Kindklassen tatsächlich nicht in der Lage, zu entscheiden, welches spezifische Verhalten sie wählen sollten. Angenommen, wir haben zwei Klassen: Toaster und NuclearBomb:

public class Toaster {


 public void on() {

       System.out.println("The toaster is on. Toast is being prepared!");
   }

   public void off() {

       System.out.println("The toaster is off!");
   }
}


public class NuclearBomb {

   public void on() {

       System.out.println("Boom!");
   }
}
Wie du siehst, haben beide eine on()-Methode. Bei einem Toaster beginnt damit der Röstvorgang. Bei einer Atombombe löst sie eine Explosion aus. Hoppla: / Nun stell dir vor, du hättest etwas erschaffen, was genau dazwischen liegt (warum auch immer!). Und dafür brauchst du die Klasse MysteriousDevice! Dieser Code funktioniert natürlich nicht, er dient nur zur Veranschaulichung, aber man könnte auf diese Idee kommen:

public class MysteriousDevice extends Toaster, NuclearBomb {

   public static void main(String[] args) {

       MysteriousDevice mysteriousDevice = new MysteriousDevice();
       mysteriousDevice.on(); // So what should happen here? Do we get toast or a nuclear apocalypse?
   }
}
Sehen wir uns das mal an. Das mysteriöse Gerät erbt gleichzeitig Toaster und NuclearBomb. Beide haben on()-Methoden. Wenn wir also die on()-Methode aufrufen, ist nicht klar, welche auf dem MysteriousDevice-Objekt aufgerufen werden soll. Es gibt keine Möglichkeit, wie das Objekt es jemals wissen könnte. Und zu allem Überfluss hat die NuclearBomb-Klasse keine off()-Methode, so dass es unmöglich wäre, das Gerät zu deaktivieren, wenn wir nicht richtig raten würden. Genau wegen dieser „Verwirrung“, bei der das Objekt nicht weiß, welches Verhalten es ausführen soll, haben die Entwickler von Java auf die Mehrfachvererbung verzichtet. Du erinnerst dich aber sicher daran, dass Java-Klassen mehrere Interfaces implementieren können. Übrigens hast du während deines Lernprozesses bereits mindestens eine abstrakte Klasse kennengelernt! Auch wenn du es vielleicht gar nicht bemerkt hast :)

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>
Es ist dein alter Freund, die Calendar-Klasse. Sie ist abstrakt und hat mehrere Kindklassen. Eine von ihnen ist GregorianCalendar. Die hast du schon in den Lektionen über Datumsangaben benutzt. :) Damit ist soweit alles klar, oder? Es stellt sich nur eine Frage: Was ist überhaupt der grundlegende Unterschied zwischen abstrakten Klassen und Interfaces? Warum enthält Java beides und nicht nur eine dieser Möglichkeiten? Das wäre doch völlig ausreichend gewesen, oder? Darüber werden wir in der nächsten Lektion sprechen! Bis dahin :)
Dieser Beitrag ist auf Englisch verfügbar.
Read the English version of this article to explore a Java abstract class. Abstraction in Java is super important and super cool!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION