Abstrakte Klassen und Methoden in Java

In diesem Beitrag betrachten wir Klassen, die mindestens eine nicht implementierte (abstrakte) Methode besitzen.

Wenn eine Klasse nicht implementierte Methoden besitzt, kann sie nicht instanziert werden und wird als abstrakte Klasse bezeichnet, da sie keine konkrete Form annehmen kann.

Im zweiten Teil dieses Beitrags zeigen wir mit Hilfe einer Beispielanwendung wie abstrakte Klassen und Methoden in Java verwendet werden.

Abstrakte Klassen in Java

Wird in Java eine Klasse mit dem Schlüsselwort abstract deklariert, gilt sie als abstrakt. Abstrakte Klassen können nicht instanziert werden.

In abstrakten Klassen können abstrakte Methoden deklariert werden. Es sind aber auch normale, konkrete Methoden erlaubt.

Java-Entwickler setzen abstrakte Klassen als Superklassen ein, die abstrakte Methoden enthalten, welche erst später von abgeleiteten Klassen implementiert werden.

Solange die abstrakten Methoden nicht in den Subklassen implementiert wurden, können weder die Superklasse noch die Subklassen instanziert werden. Existieren auch in der Subklasse noch abstrakte Methoden, dann muss auch die Subklasse als abstrakte Klasse mit dem abstract-Schlüsselwort deklariert werden.

Erst wenn alle abstrakten Methoden der Superklasse implementiert worden sind, kann die Subklasse konkret werden (instanziert werden). Diese Konkretisierung kann auch schrittweise über mehrere Vererbungsstufen erfolgen.

Abstrakte Methoden in Java

In Java werden Methoden durch das abstract-Schlüsselwort als abstrakte Methoden deklariert, die nicht implementiert werden können. Daher besitzen abstrakte Methoden keinen Methodenrumpf. Anstelle des Methodenrumpfs und den geschweiften Klammern steht ein Semikolon.

Deklaration einer abstrakten Methode: public abstract int berechneTicketpreis();.

Abstrakte Methoden können nicht aufgerufen werden, da sie über keine Implementierung verfügen. Sie müssen erst in einer abgeleiteten Klasse durch Methoden-Überlagerung nachträglich implementiert werden.

Erst dann wird eine abstrakte Methode konkret und kann aufgerufen werden.

Verwendung von abstrakten Klassen und Methoden in Java

In unserer Beispielanwendung wollen wir eine abstrakte Methode als Schnittstelle einsetzen. Dazu definieren wir eine abstrakte Superklasse, die allen abgeleiteten Klassen eine abstrakte Methode für die Berechnung der Ticketpreise zur Verfügung stellt.

Die Superklasse definiert alle gemeinsamen Eigenschaften von Tickets, wie Basispreis, Eventname und Veranstaltungsort. Die spezialisierten Subklassen implementieren dann die jeweiligen zusätzlichen Eigenschaften der verschiedenen Ticketarten (Sport-, Konzert- und Kinoticket).

Die Berechnung des Ticketpreises soll für jedes Ticket einzeln erfolgen, ist aber abhängig von der Ticketart. Daher wird die abstrakte Methode berechneTicketpreis() schon in der Superklasse (Basisklasse) deklariert, aber erst in den abgeleiteten Subklassen implementiert.

Der folgende Quellcode enthält die Implementierungen der Subklassen SportTicket, KonzertTicket, KinoTicket und AutoKinoTicket, sowie die Implementierung ihrer abstrakten Superklasse Ticket. Die Berechnung des Gesamtticketumsatzes wird in der Klasse TicketpreisBerechnung durchgeführt, in der auch die main-Methode definiert ist.

Für die Berechnung der Ticketpreise wird ein Array vTickets erzeugt, das alle verkauften Tickets enthält und dazu mit konkreten Untertypen der Klasse Ticket gefüllt wird. Anschließend wird für alle Array-Elemente (Tickets) der jeweilige Ticketpreis berechnet mit Hilfe der Methode berechneTicketpreis(), die in jeder Subklasse entsprechend der Ticketart implementiert ist.

Beispielanwendung für abstrakte Klassen und Methoden in Java

/*
* Beispielanwendung für abstrakte Klassen und Methoden in Java
*/

abstract class Ticket
{
   String eventOrt;
   String eventName;
   int basisPreis;
   int ticketPreis;

   public Ticket (String ort, String name, int preis)
   {
      eventOrt = ort;
      eventName = name;
      basisPreis = preis;
   }

   // Deklaration der abstrakten Methode, macht die Methode für Subklassen bekannt
   // implementiert die Methode aber nicht, dies muss noch in den Subklassen erfolgen
   public abstract int berechneTicketpreis();

   public void printTicketdaten()
   {
      String text;
      text  = "Eventort: " + eventOrt;
      text += ", Eventname: " + eventName;
      text += ", Preis: " + ticketPreis + " Euro";
      System.out.println(text);
   }
}

class SportTicket extends Ticket
{
   int pokalStufe;

   public SportTicket(String ort, String name, int preis, int stufe)
   {
      super(ort, name, preis);
      pokalStufe = stufe;
   }

   // Konkretisieren der abstrakten Methode durch Implementieren in der Subklasse
   public int berechneTicketpreis()
   {
      ticketPreis = basisPreis + (10*pokalStufe);
      
      return ticketPreis;
   }
}

class KonzertTicket extends Ticket
{
   int sitzReihe;

   public KonzertTicket(String ort, String name, int preis, int reihe)
   {
      super(ort, name, preis);
      sitzReihe = reihe;
   }

   public int berechneTicketpreis()
   {
      ticketPreis = basisPreis*(1 + 1/sitzReihe);
      
      return ticketPreis;
   }
}

class KinoTicket extends Ticket
{
   int filmDauer;

   public KinoTicket(String ort, String name, int preis, int dauer)
   {
      // Aufruf des Superkonstruktors (hier Konstruktor der Ticket-Klasse)
      super(ort, name, preis);
      filmDauer = dauer;
   }

   public int berechneTicketpreis()
   {
      ticketPreis = basisPreis;

      if (filmDauer > 150)
      {
         ticketPreis += 3;
      }
      
      return ticketPreis;
   }
}

class AutoKinoTicket extends KinoTicket
{
   int anzahlPersonen;

   public AutoKinoTicket(String ort, String name, int preis, int dauer, int personen)
   {
      // Aufruf des Superkonstruktors (hier Konstruktor der KinoTicket-Klasse)
      super(ort, name, preis, dauer);
      anzahlPersonen = personen;
   }

   public int berechneTicketpreis()
   {
      // Aufruf der Methodenimplementierung der Superklasse (hier KinoTicket-Klasse)
      ticketPreis = super.berechneTicketpreis() * anzahlPersonen;
      
      return ticketPreis;
   }
}

public class TicketpreisBerechnung
{
  private static Ticket[] vTickets;  // Array aller verkauften Tickets
  private static int umsatz; // Gesamtumsatz aller Ticketverkäufe
  private static final int ANZ_TICKETS; // Konstante

  // statischer Initialisierer initialisiert statische Variablen und Konstanten
  static
  {
     umsatz = 0;
     ANZ_TICKETS = 10;
     vTickets = new Ticket[ANZ_TICKETS];
  }

  public static void main (String[] args)
  {
     vTickets[0] = new SportTicket("LONDON", "CHE-MAN", 50, 4);
     vTickets[1] = new KonzertTicket("BERLIN", "BACH", 74, 37);
     vTickets[2] = new KinoTicket("DRESDEN", "HONEY", 9, 165);
     vTickets[3] = new AutoKinoTicket("PRAG", "E.T.", 5, 115, 4);
     vTickets[4] = new SportTicket("ERFURT", "ERF-DYN", 12, 0);
     vTickets[5] = new KonzertTicket("STUTTGART", "TARZAN", 99, 8);
     vTickets[6] = new SportTicket("BARCELONA", "BAR-MAD", 125, 7);
     vTickets[7] = new KonzertTicket("PORTO", "MARIA PIRES", 79, 1);
     vTickets[8] = new AutoKinoTicket("MOSKAU", "AVATAR", 9, 162, 2);
     vTickets[9] = new KinoTicket("PARIS", "PANEM", 10, 142);

     System.out.println("\n------- Allgemeine Ticketdaten -------\n");

     for (int i=0; i<vTickets.length; i++)
     {
        // Nutzen von Polymorphismus und Late Binding, dadurch wird dank der
        // dynamischen Methodensuche die zum Typ des konkreten Objekts passende
        // Methodenimplementierung aufgerufen  
        umsatz += vTickets[i].berechneTicketpreis();
     }

     // die erweiterte for-Schleife sorgt für leichtere Lesbarkeit
     for (Ticket element : vTickets)
     {
        element.printTicketdaten();
     }

     System.out.println("\nGesamtumsatz aller Ticketverkaeufe: " + umsatz + " Euro");
  }
}

Die obere Beispielanwendung nutzt die Möglichkeiten des Polymorphismus von Java. In der Array-Variable vTickets vom Typ der Superklasse Tickets werden zur Laufzeit verschiedene Objekte der Subklassen SportTicket, KonzertTicket, KinoTicket und AutoKinoTicket abgelegt.

Da bereits die Superklasse Ticket die Deklaration der Methode berechneTicketpreis enthält, ist diese Methode dem Java-Compiler für Objektvariablen vom Typ Ticket bekannt. Die Methode ist in der Superklasse noch abstrakt, wird aber von den Subklassen nachträglich durch Methodenüberlagerung implementiert.

Da das Array vTickets nur Objekte vom Typ der Subklassen SportTicket, KonzertTicket, KinoTicket und AutoKinoTicket enthält und in jeder Subklasse die Methode berechneTicketpreis implementiert ist, kann zur Laufzeit für jedes Array-Element die Methode berechneTicketpreis aufgerufen werden. Dazu wird mit Hilfe der dynamischen Methodensuche zur Laufzeit entschieden welche implementierte Variante der Methode berechneTicketpreis für das jeweilige Array-Element aufgerufen werden soll.

Es wird für jedes Array-Element die passende Berechnung durchgeführt. Unabhängig davon, ob in einem Array-Element ein SportTicket, KonzertTicket, KinoTicket und AutoKinoTicket gespeichert ist, führt der Aufruf von berechneTicketpreis durch Late Binding die zum Datentyp des konkreten Objekts passende Berechnung aus.

Es können auch weitere Ticketarten hinzugefügt oder bestehende Arten spezialisiert werden, ohne das dabei eine Veränderung an der Routine vorgenommen werden muss. Die Subklasse AutoKinoTicket leitet sich von der Klasse KinoTicket ab und besitzt eine eigene Implementierung der Methode berechneTicketpreis.

Hinweis: Mit Hilfe des Schlüsselworts super können die Anweisungen des Superklassen-Konstruktors genutzt und somit Quellcode wiederverwendet werden. Achtung! Der Aufruf des Superkonstruktors muss, wie im Quellcode angegeben, erfolgen. Andernfalls erhält man einen Compiler-Fehler, da der parameterlose Standard-Konstruktor hier nicht vom Java-Compiler erstellt wird und nur der parametrisierte Konstruktor existiert.

Starten wir nun die Beispielanwendung. Die dabei erzeugte Textausgabe ist in der unten abgebildeten Kommandozeilenausgabe dargestellt:

Java abstrakte Klassen und Methoden

Java abstrakte Klassen und Methoden – Ausgabe der Beispielanwendung

Mit dem folgenden Link gelangt ihr wieder zurück zum Java Kurs für Programmieranfänger.


Comments 1

  1. Pingback: Klassen in Java: Die Grundlagen der objektorientierten Programmierung

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.