Interface als Flag nutzen am Beispiel des Cloneable-Interfaces in Java

In Java können Interfaces als logische An- und Aus-Schalter verwendet werden, die zur Kompilier- und Laufzeit abgefragt werden können.

In diesem Beitrag möchten wir ein solches Flag-Interface am Beispiel des Interface Cloneable vorstellen. Das Cloneable-Interface ist ein Flag (An/Aus-Schalter) für die in der Klasse Object implementierte Methode clone(). Die Methode clone() ist in der Object-Klasse native implementiert, um die Performance zu verbessern.

plhq_teaser_hbox_gelb_fotolia_RA Studio_46292813

Unser großes
Android Online-Kurs
Gesamtpaket



Weitere Infos

Das Cloneable-Interface wird als Indikator genutzt, mit dem überprüft wird, ob eine abgeleitete Klasse die Fähigkeit besitzt eine eigene Objektkopie herzustellen. Ist das Cloneable-Interface in der abgeleiteten Klasse nicht implementiert, wird beim Aufruf von clone() eine CloneNotSupportedException ausgelöst.

Dadurch wird dem Entwickler mitgeteilt, dass die abgeleitete Klasse nicht über die Fähigkeit verfügt eigene Objekte elementweise zu kopieren (clonen).

Shallow Copy vs. Deep Copy

Durch Aufruf der clone-Methode der Klasse Object werden nur die Verweise (Referenzen) auf die Membervariablen kopiert, nicht aber die darin hinterlegten Objekte.

Man spricht in diesem Fall von einer flachen Kopie (shallow copy), bei der nur die primitiven Datentypen und die Objektreferenzen kopiert werden, nicht aber die dahinter stehenden Objekte.

Somit würden sich die originalen und kopierten Objektvariablen die gleichen Objekte teilen.

Sollen auch die Objekte kopiert werden, muss eine tiefe Kopie (deep copy) angelegt werden. Dabei muss jedes in einer Membervariable hinterlegtes Objekt selbst wieder kopiert (gecloned) werden.

Da Objekte selbst weitere Objekte enthalten können, müssen auch diese kopiert werden und zwar elementweise. Dieser Kopierprozess wird in der Regel rekursiv ausgeführt, d.h. die clone-Methode ruft sich selbst auf bis sie terminiert.

Java-Anwendung: Erzeugen einer tiefen Kopie (Deep Copy)

Android Apps Programmieren Online-Kurs

Unser großes
Android Online-Kurs
Gesamtpaket



Weitere Infos

In der folgenden Beispielanwendung wird das Cloneable-Interface von unserer Klasse Kettenelement implementiert und mit Hilfe der Methode clone() eine tiefe Kopie unserer Objekte erstellt.

Unsere Klasse Kettenelement implementiert das Interface Cloneable, indem es die Methode clone() mit einer eigenen Methodenversion überlagert. Das Cloneable-Interface wird in Java als Flag verwendet. Da unsere Klasse das Interface implementiert, ist der Cloneable-Schalter jetzt auf AN gestellt. Dies wird als Besitz der Fähigkeit, eine vollständige Objektkopie herstellen zu können, gedeutet.

Ob unsere Klasse diese Fähigkeit tatsächlich besitzt, ist von unserer Implementierung der Methode clone() abhängig.

Unsere Klasse Kettenelement stellt ein Element einer gerichteten, unidirektionalen Kette dar. Dabei besitzt jedes Element genau einen Nachfolger, der auch vom Typ Kettenelement ist.

Beim Kopieren wird das aktuelle Objekt zuerst flach kopiert (shallow copy), durch Aufrufen der clone-Methode der Vaterklasse super.clone();. Danach wird unsere Methode clone() rekursiv aufgerufen, um Kopien der Nachfolge-Elemente herzustellen.

Das letzte Kettenelement enthält keinen Nachfolger, sondern stattdessen den Wert null. Sobald wir in unserem Kopiervorgang das letzte Element der Kette erreicht haben, endet die Rekursion und somit auch der Kopiervorgang.

Mit Hilfe unserer clone-Methode werden das aktuelle Kettenelement und alle nachfolgenden Elemente kopiert. Dabei entsteht eine vollständige Kopie der gewählten Teilkette, die keine gemeinsamen Objekte mit der Originalkette besitzt. Die Objekte wurden elementweise als tiefe Kopie (deep copy) kopiert.

Der folgende Quelltext enthält unsere Klasse Kettenelement und die Testklasse CloneTest:

Beispielanwendung: Implementierung des Cloneable Interfaces

/*
* Beispielanwendung: Implementierung des Cloneable Interfaces
*/

class Kettenelement implements Cloneable
{
  String name;
  Kettenelement next;

  public Kettenelement (String name, Kettenelement nf)
  {
    this.name = name;
    next = nf;
  }

  public Object clone()
  {
    try
    {
      Kettenelement clonedElement = (Kettenelement) super.clone();

      if (next != null)
      {
        clonedElement.next = (Kettenelement) next.clone();
      }

      return clonedElement;
    }
    catch (CloneNotSupportedException e)
    {
      throw new InternalError();
    }
  }
}

public class CloneTest
{  
  public static void main (String[] args)
  {
    Kettenelement lastElement  = new Kettenelement("3. Element", null);
    Kettenelement midElement   = new Kettenelement("2. Element", lastElement);
    Kettenelement firstElement = new Kettenelement("1. Element", midElement);

    System.out.println("\n" + firstElement.name + "               : " + firstElement);
    System.out.println("Nachfolger ist "+firstElement.next.name+": "+firstElement.next);

    System.out.println("\n" + midElement.name + "               : " + midElement);
    System.out.println("Nachfolger ist "+midElement.next.name+": "+midElement.next);

    System.out.println("\n" + lastElement.name + "               : " + lastElement);
    System.out.println("Kein Nachfolger          : " + lastElement.next);


    Kettenelement clonedElement = (Kettenelement) midElement.clone();
    clonedElement.name += " (cloned)"; 

    System.out.println("\n\n" + clonedElement.name + "      : " + clonedElement);
    System.out.println("Nachfolger ist " + clonedElement.next.name + ": " + clonedElement.next);


    System.out.println("\n\n" + firstElement.name + "               : " + firstElement);
    System.out.println(midElement.name + "               : " + midElement);
    System.out.println(lastElement.name + "               : " + lastElement);

    System.out.println("Cloned Element           : " + clonedElement);
    System.out.println("Cloned Element Nachfolger: " + clonedElement.next);
  }
}

Anhand der Kommandozeilenausgabe kann man erkennen, dass das kopierte Kettenelement clonedElement eine tiefe Kopie vom zweiten Element midElement ist. Auch der Nachfolger von clonedElement ist eine tiefe Kopie des dritten Elements lastElement.

In der folgenden Abbildung ist die Konsolenausgabe unserer Beispielanwendung dargestellt:

Java Interface Cloneable Flag

Interfaces als Flags benutzen in Java – Ausgabe der Beispielanwendung

Dieser Link führt zurück zum Java-Kurs für Programmierneulinge.


Comments 2

  1. Pingback: Einführung in Java Design Patterns (Software Entwurfsmuster)

  2. Pingback: Interfaces (Schnittstellen) in Java

Schreibe einen Kommentar

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