11.1 Schnittstellen (Interfaces)

11.1 Schnittstellen (Interfaces)

Javaklassen enthalten Methoden und Datenfelder. Die öffentlich zugänglichen Methoden und Datenfelder bilden die Schnittstellen einer Klasse. Schnittstellen sind wichtige Hilfsmittel bei Entwurf komplexer Softwaresysteme. Sie erlauben es unterschiedliche Subsysteme definiert zu koppeln (Coupling). Die Wechselwirkung zwischen diesen Subsystemen wird auch Coherence oder Cohesion genannt.

Schnittstellen unterstützen das Konzept des Information Hiding und sollten möglichst stabil sein. Stabile Schnittstellen erlauben es mehreren Teams unabhängig von einander zu arbeiten. Sie erlauben eine Trennung von

  • Spezifikation und
  • Implementierung

Schnittstellen (engl. Interfaces) sind ein wichtiges Sprachmittel beim Entwurf von Softwaresystemen.

Sie enthalten daher nur die für die Spezifikation wichtige Elemente:

  • Methodendeklarationen
  • öffentliche Attribute

Klassen hingegen enthalten die Spezifikation, alle Attribute sowie zusätzlich die Implementierung der Methoden durch die Methodenrümpfe.

Javaschnittstellen (Interfaces)

Java bietet im Gegensatz zu C++ das Schlüsselwort interface zur Beschreibung von Schnittstellen. Java-Interfaces muss man gedanklich als abstrakte Klassen verstehen, die selbst über keine Implementierung von Methoden verfügen.

Klassen enthalten die Spezifikation und zusätzlich die Implementierung der Methoden durch die Methoden Rümpfe/Blöcke. Schnittstellen in Java enthalten (in der Regel, siehe unten) nur die Spezifikation.

Eigenschaften Klasse abstrakte Klasse Schnittstelle
Öffentliche Attribute Ja Ja Ja
Private Attribute Ja Ja Nein
Methodenköpfe (die Spezifikation) Ja Ja Ja
Methodenrumpf Ja Nein (bei abstr. Meth.) Nein (in der Regel)
kann von Klasse erben Ja Ja Nein
kann von Schnittstelle erben Nein Nein Ja
kann eine oder mehrere Schnittstelle implementieren Ja Ja Nein

 Für Java-Schnittstellen gilt:

  • alle Attribute müssen öffentliche, finale und initialisierte Konstanten sein
  • alle Methoden sind abstrakt (abstract), öffentlich (public), nicht final und nicht statisch
  • sie geben keine Konstruktoren vor. Die Gründe sind:
    • Konstruktoren haben immer den Namen der aktuellen Klasse.
    • Ein Konstruktor in einer Implementierung kann nicht den Namen der implementierten Schnittstelle besitzen.

Notation einer Java-Schnittstelle

[public] interface Interface-name [extends Interface1, Interface2, ... ];
  • Das Schlüsselwort public erlaubt wie auch bei Klassen das Interface ausserhalb des zugehörigen Pakets zu benutzen
  • dem Schlüsselwort interface folgt der Name der Schnittstelle und
  • eine optionale Liste von Schnittstellen von denen die Schnittstelle erbt. Diese Liste von Schnittstellen wird mit dem Schlüsselwort extends eingeleitet

Schnittstellen können nur von Schnittstellen abgeleitet werden.

Klassen können nur von Klassen abgeleitet werden. Klassen implementieren jedoch beliebig viele Schnittstellen durch das Schlüsselwort implements.

[public|private|protected] class Klassenname1 extends Klassenname2 [implements Interface1, Interface2, ....]

Die Klasse Klassenname1 wird aus Klassenname2 abgeleitet und implementiert (Schlüsselwort implements) optional eines oder mehrere Interfaces.

Beispiel

Personen (class Person), sowie Lieferanten (class Supplier) implementieren das Interface BankAccount. BankAccount muss mit einer IBAN Nummer und dem Namen einer Bank umgehen können. Das Interface macht keinerlei Vorgaben zum Setzen dieses Daten. Das Erfassen dieser Daten obliegt dem Implementierer der Schnittstelle.

interface BankAccount {
   public long iban();
   public String bank();
}

class Person implements BankAccount {
   private String myIban;
   private String myBank;
   public name()  { // Implementierung
   }
   public long iban() {
      ...
      return myIban;
   }
   public String bank() {
      ...
      return myBank;
   }
}

class Supplier implements BankAccount {
   private String supplierIban;
   private String supplierBank;
   public String getLicense() { //Implementierung
      }
   public long iban() {
      ...
      return supplierIban;
   }
   public String bank() {
      return supplierBank;
   }
}
...
BankAccount b1 = new Person();
BankAccount b2 = new Supplier();
...
System.out.println(b1.bank());
System.out.println(b2.bank());

Die Klasse Person implementiert alle Methoden von BankAccount und verhält sich in allen Aspekten wie das Interface.

Für Klassen die Schnittstellen(Interfaces) implementieren gilt:

  • sie können beliebig viele Interfaces gleichzeitig implementieren.
  • sie implementieren alle Methoden und Attribute des Interface
    • implementieren sie nicht alle Aspekte des Interface müssen sie als abstrakte Klasse deklariert werden. Erst abgeleitete Klassen die alle Aspekte der Schnittstelle zur Verfügung stellen müssen nicht mehr abstrakt sein.

Schnittstellen und Vererbung

  • Schnittstellen können von Schnittstellen erben (Schlüsselwort extends)
    • Dies deutet für den Entwickler, dass er alle Methoden der Vererbungshierarchie implementieren muss. In diesen Aspekt verhalten sich Schnittstellenmethoden wie abstrakte Methoden.
  • Klassen können nicht von Schnittstellen erben. Sie implementieren Schnittstellen!

UML Notation

In UML Klassendiagrammen wird die Java Interface-beziehung als Verfeinerung mit gestrichelten Pfeilen gekennzeichnet: Alternativ kann man die zu implementierende Klasse auch mit einem Strich und einem Kreis darstellen:

UML Diagramm mit Java Schnittstellen

alternative Schnittstellenbeschreibung in UML

Anwendung von Schnittstellen (Interfaces)

Schnittstellen (Schlüsselwort interface) und Vererbung (Schlüsselwort extends) sind zwei Vererbungskonzepte in Java. Sie haben unterschiedliche Zielsetzungen bezüglich Strukturierung und Planung.

  • Schnittstellen (Interfaces)
    • fördern modularen Aufbau auf Softwaresystemen
      • erlauben unabhängige Implementierung und vereinfachen den Austausch von Schnittstellenimplementierungen
    • fordern nur die Implementierung bestimmter Methoden.
    • entkoppeln Systeme und fördern die genaue Spezifikation von benötigten Diensten (Verträge!)
    • schaffen Strukturen die in späteren Phasen der Softwareentwicklung die Komplexität der Implementierung senken
  • Vererbung
    • erleichert Codewiederverwendung
    • führt zu einer engeren Kopplung von Systemen.
      • Da immer eine Implementierung der Oberklasse genutzt werden muss. Man muss z.Bsp. den Konstruktor der Oberklasse immer nutzen
      • Eine Unterklasse muss immer alle Eigenschaften der Oberklasse aufweisen
  Vererbung (extends) Schnittstelle (interface)
Spezifikation wird von der Oberklasse vorgegeben wird vom Interface vorgegeben
Programmcode wird von der Oberklasse vorgegeben.
wird von einer abstrakten Oberklasse gefordert
Interface fordert eine Implementierung
Mehrfachvererbung/Implementierung nein ja
Abhängigkeit der Unterklasse/Interfaceimplementierung hängt stark von der Oberklasse ab hängt schwächer vom zu implementierenden Interface ab

Implikationen

Schnittstellen (Interfaces) müssen Aufgrund ihrer großen Bedeutung für den Entwurf von Softwaresystemen sehr sorgsam entworfen werfen. Änderungen in Schnittstellen führen dazu, dass alle Klassen die eine Schnittstelle  (Interface) implementieren, bei einer Änderung vom Laufzeitsystem nicht mehr akzeptiert werden. Bei einer Schnittstellenänderung müssen alle Klassen der neuen Schnittstelle angepasst und neu übersetzt werden. 

Schnittstellen versus Vererbung mit Polymorphismus

Schnittstellen sind oft eine bessere Lösung für langlebige Softwaresysteme. Die enge Kopplung an die vererbenden Basisklassen werden sich ändernden Anforderungen auch oft ein Hindernis. Der Vorteil alle ererbten Methoden direkt benutzen zu können, kann hier auch zum Nachteil werden, insbesondere wenn Klassenhierarchien tief und unübersichtlich sind.

Die Konzepte von

  • Assoziation und
  • Schnittstellen

führen oft zu einem initial höheren Implementierungsaufwand. Sie bieten jedoch eine bessere Entkopplung von Softwarekomponenten. 

Weiterführende Quellen: "Why extends is evil"

Genau hingeschaut: Methodenimplementierungen von Schnittstellen

Weiter oben wird behauptet, dass in der Deklaration einer Schnittstelle keine Implementierung von Methoden möglich sind. Diese Aussage ist seit Java 8 so nicht mehr korrekt. Sie gilt aber nach wie vor für sehr, sehr viele Anwendungsfälle.

Die neuen Möglichkeiten von "default" Methoden in Schnittstellen werden im Rahmen dieser Vorlesung nicht beachtet. Man sollte diesen Sonderfall kennen. Für das grundlegende Verständnis des Schnittstellenkonzepts ist dieser "exotische" Anwendungsfall aber nicht notwendig.

Hintergrund

Vor Java 8 waren alle Methoden eines Interfaces abstrakt. Das heißt, es lag nur der Methodenkopf mit der Signatur vor. Seit Java 8 kann man in Interfaces

  • default-Methoden implementieren
  • statische Methoden implementieren

Die Notwendigkeit dieser Konzepte entstanden durch die Integration von Lamdba-Ausdrücken (JSR 335) . Man benötigt diese Konzepte um die Rückwärtskompatibilität zu älteren Javaimplementierungen zu gewährleisten. Es beruht auf dem Problem der Java Interface-Evolution bei der Benutzung der Collection Klassen in Lamdba-Ausdrücken. Angelika Langer hat dieses Problem sehr gut in einem deutschsprachigen Internetartikel im Java Magazin beschrieben.

Stefan Schneider Thu, 05/26/2011 - 20:03

11.1.1 Beispiele Schnittstellenanwendung

11.1.1 Beispiele Schnittstellenanwendung

 Serialisierung

Serialisierung ist ein Beispiel bei dem man gut sieht wie eine Klasse durch Implementierung einer Schnittstelle wichtige Eigenschaften gewinnen man. Man kann sie in einer Datei speichern oder in einen beliebigen Stream stecken!

Java bietet die Möglichkeit Objekte im Hauptspeicher in einen seriellen Datenstron zu konvertieren den man zum Beispiel in Dateien schreiben kann. Diese Technologie nennt man Serialisierung(engl. Serialization siehe Wikipedia). Java kann Klassen serialisieren die die Schnittstelle Serializable implementieren.

Java kann Objekte serialisieren wenn

  • die entsprechende Klasse die Schnittstelle Serializable implementiert und
  • wenn alle Attribute die auf Objekte zeigen auch die Schnittstelle Serializable implementieren

Das folgende Beispielprogramm ist in der Lage eine Person und ihre Adresse in eine Datei zu schreiben und wieder zu lesen.

Alles was man tun muss um die Klassen Person und Adresse serialisierbar zu machen ist die Schlüsselworte "implements Serializable" hinzuzufügen

 Heap Diagramm mit vier Objekten zur Serialisierung
  1. Im Hauptprogramm wird eine Person Urmel angelegt die auf eine Adresse Lummerland zeigt
  2. Die Methode Methode schreiben() schreibt alle von p referenzierten Objekte in eine Datei serialisiert.ser
  3. Die Methode lesen() liest die Person und alle referenzierten Objekte aus der Datei zurück
  4. p1 zeigt nun auf eine neue Person mit eigenen referenzierten Objekt Adresse

 

 Überlegungen:

  • Die Schnittstelle Serializable fordert keine Methoden zu implementieren!
  • Warum sind 4 Objekte in der Datei?
  • Was würde geschehen wenn die die Klasse Person mit Stammbaum aus der ersten Vorlesung serialisieren würde?
  • Was würde geschehen wenn man weitere Attribute mit Basistypen zu den Klassen hinzufügt?
  • Was geschieht wenn man auf eine Klasse referenziert die nicht serialisierbar ist?

Klasse Serialisierung

package s1.block11;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
*
* @author s@scalingbits.com
*/
public class Serialisierung {
/**
* Erzeuge eine Person und die Adresse. Schreibe sie in eine
* Datei und lese sie aus der Datei
* @param args
*/
   public static void main(String[] args) {
   final String meineDatei = "serialisiert.ser";
   // Erzeuge das Objekt der Person und das Objekt mit der Adresse
   Person p = new Person("Urmel", new Adresse("Lummerland"));
   System.out.println("Zu schreibende Person " + p.name +
      " wohnt in " + p.wohnort.stadt);
   // Schreibe die Objekte in eine Datei
   schreiben(p, meineDatei);
   Person p1 = (Person) lesen(meineDatei);
   System.out.println("Gelesene Person " + p1.name +
      " wohnt in " + p1.wohnort.stadt);
   System.out.println("Die geschrieben Adresse und die gelesene"+
      " Adresse " + p.wohnort.stadt + " sind" +
      (p.wohnort==p1.wohnort? " " : " nicht ") +
      "identisch");
   }
   /**
   * Diese Methode schreibt ein beliebiges serialisierbares Objekt
   * in eine Datei
   * @param o Objekt welches in eine Datei geschrieben wird.
   * Es muss serialisierbar sein!
   * @param datei Die Datei in die geschrieben werden soll
   */
   public static void schreiben(Object o, String datei) {
      try {
         // Erzeuge einen Stream der in eine Datei geleitet wird
         FileOutputStream fileOut = new FileOutputStream(datei);
         // Erzeuge einen Stream der Objekte in einen Filestream leitet
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         // Schreibe ein beliebiges Objekt in diesen Objectstream
         out.writeObject(o);
        // Schliesse Stream
        out.close();
        // Schliesse Datenstrom. Die letzten Bytes werden so raus geschrieben
          fileOut.close();
        System.out.println("Serialisierte Daten sind gespeichert in Datei "
            + datei);
      } catch (IOException i) { // Hier können Ausnahmen auftreten
         i.printStackTrace();
      }
   }
   /**
   *
   * @param datei die Datei aus der gelesen werden soll
   * @return
   */
   public static Object lesen(String datei) {
      System.out.println("Lesen aus " + datei);
      Object o;
      try { // Hier können Ausnahmen Auftreten
         // Öffne Datei aus der gelesen werden soll
         FileInputStream fileIn = new FileInputStream(datei);
         // Erzeuge Objectstream der aus Datei liest
         ObjectInputStream in = new ObjectInputStream(fileIn);
         //Lies Objekt aus Stream
         o = in.readObject();
         // Schließe Objectstream
         in.close();
         // Schließe Datei
         fileIn.close();
      } catch (IOException i) { // Wird ausgeführt wenn Probleme auftreten
         i.printStackTrace();
         return null;
      } catch (ClassNotFoundException c) {
         System.out.println("Gelesene Klasse nicht gefunden");
         c.printStackTrace();
         return null;
   }
   return o;
   }
}

 

Klasse Adresse

package s1.block11;

import java.io.Serializable;

/**
*
* @author s@scalingbits.com
*/
public class Adresse implements Serializable {
   String stadt;
   public Adresse (String s)
   {
      stadt =s;
   }
}

Klasse Person

package s1.block11;

import java.io.Serializable;

/**
*
* @author s@scalingbits.com
*/
public class Person implements Serializable {
   String name;
   Adresse wohnort;
   public Person(String n, Adresse w) {
      wohnort = w;
      name=n;
   }
}

Weiterführende Übungen und Überlegungen zu diesem Beispiel

  1. Was geschieht wenn zwei Personen auf das gleiche Adressobjekt zeigen? Wird die Adresse zweimal gespeichert oder nur einmal? Selbst wenn die Adresse nur einmal gespeichert wird. Was geschieht beim Lesen der serialisierten Datei. Wird das Addressobjekt verdoppelt?
  2. Was geschieht beim Übersetzen und Ausführen wenn man den Term "implements Serializable" in der Klasse Adresse oder Person weglässt?
  3. Versioning Problem: Was geschieht wenn man die Objekte in eine Datei serialisiert. Dann ein Attribut zu einer Klasse hinzufügt oder weglässt. Dann die serialisierten Objekt wieder einliest?
  4. Was muss man tun wenn man ein Attribut (zBsp. stadt) nicht speichern möchte? Suchen Sie in der Java-Spezifikation oder mit einer Suchmaschine...

 

Stefan Schneider Sun, 03/01/2015 - 12:28

11.1.2 Übung: Schnittstellen und abstrakte Klassen

11.1.2 Übung: Schnittstellen und abstrakte Klassen

Übung 1: Implementiere die Klasse Dollar aus der abstrakten Klasse Number

Die Java Laufzeitumgebung bietet die abstrakte Klasse Number aus der alle Zahlentypen abgeleitet werden.

Implementieren Sie eine Klasse Dollar die aus der Klasse Number abgeleitetet wird.

  • Die Klasse soll aus Sicherheitsgründen mit unveränderbaren Attributen belegt sein. Der Wert des Objekts darf nur im Konstruktur gesetzt werden.
  • Im Konstruktor soll der Betrag nach Dollar und Centbeträgen getrennt erfasst werden. Unsinnige Centbeträge sollen auf Null Cent gesetzt werden.

Vorgehen:

  • Arbeiten Sie am besten im Paket Kurs2.Schnittstelle. Beim Kopieren sind dann keine Anpassungen notwendig.
  • Nutzen Sie die Klasse Kurs2.SchnittstelleTestDollar zum Testen Ihrer Implementierung.
  • Legen Sie eine Klasse Kurs2.Schnittstelle.Dollar an, die aus der Klasse Number abgeleitet wird.
  • Tipp: Verwaltung des Betrags. Es ist einfacher den gesamten Betrag als Centbetrag in einem long zu speichern
  • Geben Sie bei allen Methoden die ganze Zahlen implementieren nur den Dollarbetrag aus
  • Geben Sie bei den Fließkommamethoden den Dollar und Centbetrag auf 2 Nachkommastellen aus (Rundungsfehler können ignoriert werden)

Tipp: Vorsicht beim Berechnen des Centbetrag im Konstruktor bei negativen Werten. Es gilt:

  • $3.25 = 3 *100 + 25 cents
  • $-3.25 = -3 * 100 - 25 cents
  • daher gilt die Rechenregel cents = signum(dollar)*( abs(dollar)*100+abs(cents) )

Nutzen Sie hierfür die statischen Importdeklarationen:

import static java.lang.Math.abs;
import static java.lang.Math.signum;

Das Testprogramm TestDollar

package s1.block11;
public class TestDollar {
/**
*
* Das Hauptprogramm benötigt keine Parameter
* Es testet die wichtigsten Eigenschaften der Klasse Dollar
*/
public static void main(String[] args) {
System.out.println("Phase 1: Einfache Tests");
Dollar konto1 = new Dollar(172, 12);
long d1 = konto1.longValue();
System.out.println("Dollar.longValue().Sollwert 172; Istwert: " + d1);
double d2 = konto1.doubleValue();
System.out.println("Dollar.doubleValue().Sollwert 172.12; Istwert: " + d2);
float d3 = konto1.floatValue();
System.out.println("Dollar.floatValue().Sollwert 172.12; Istwert: " + d3);
System.out.println("Phase 2: Härtere Tests");
Dollar konto2 = new Dollar(-380, 25);
d1 = konto2.longValue();
System.out.println("Dollar.longValue().Sollwert -380; Istwert: " + d1);
d2 = konto2.doubleValue();
System.out.println("Dollar.doubleValue().Sollwert -380.25; Istwert: " + d2);
d3 = konto2.floatValue();
System.out.println("Dollar.floatValue().Sollwert -380.25; Istwert: " + d3);
Dollar konto3 = new Dollar (-382,225);
d1 = konto3.longValue();
System.out.println("Dollar.longValue().Sollwert -382; Istwert: " + d1);
d2 = konto3.doubleValue();
System.out.println("Dollar.doubleValue().Sollwert -382; Istwert: " + d2);
d3 = konto3.floatValue();
System.out.println("Dollar.floatValue().Sollwert -382; Istwert: " + d3);
}
}

Die Ausgaben sollten etwa wie folgt aussehen:

Phase 1: Einfache Tests
Dollar.longValue().Sollwert 172; Istwert: 172
Dollar.doubleValue().Sollwert 172.12; Istwert: 172.12
Dollar.floatValue().Sollwert 172.12; Istwert: 172.12
Phase 2: Härtere Tests
Dollar.longValue().Sollwert -380; Istwert: -380
Dollar.doubleValue().Sollwert -380.25; Istwert: -380.25
Dollar.floatValue().Sollwert -380.25; Istwert: -380.25
Dollar.longValue().Sollwert -382; Istwert: -382
Dollar.doubleValue().Sollwert -382; Istwert: -382.0
Dollar.floatValue().Sollwert -382; Istwert: -382.0

Übung 2: Implementieren von Schnittstellen

Implementieren Sie die Klasse Euro. Die Klasse Euro

  • wird aus der Klasse Number abgeleitet
  • implementiert die Schnittstelle Comparable
  • implementiert die SchnittStelle Waehrung
  • soll noch die Methode public String toString() überschreiben um einen formatierten Wert ausgeben zu können.

Hinweise zur Implementierung der abstrakten Klasse Number

Kopieren Sie sich alle abstrakten Methoden der Klasse Number in die Klasse Dollar! Somit stellen Sie sicher, dass die Signatur identisch ist und das Überschreiben funktioniert.

Hinweise zur Implementierung der Schnittstelle Waehrung

Geben Sie bei der Methode symbol() ein "€" Zeichen zurück.

Implementierung der Methode mult(double faktor).

Diese Methode dient der Zinsberechnung. Durch die Multiplikation mit dem Wert 1.03 soll man den Wert um 3% Vergrößeren oder Verkleinern können.

  • Erzeugen Sie ein neues Objekt vom Typ Euro und geben Sie es direkt zurück. Verändern Sie da aktuelle Objekt nicht.
  • Berechnen des Centbetrags: Nehmen Sie den Absolutwert ihres neu berechneten Centbetrags und dann davon den Rest einer Division durch 100 (Modulo). Negative Beträge werden so korrekt berechnet.

Hinweise zu Implementierung der Schnittstelle Comparable()

Diese Schnittstelle erlaubt vielen Hilfsklassen in Java eine Sortierung vorzunehmen. Die Methode gibt abhängig vom Vergleich der Objekte negative, postive oder einen Nullwert zurück. Die Logik des Vergleichs liegt in der Verantwortung des Programmieres.

Tipp: Sie müssen in der Lage sein einen Euro gegen ein beliebiges Objekt zu vergleichen!

  • Prüfen Sie den Typ des Objekts vor dem Vergleich (siehe Kapitel Polymorphismus)
    • Geben Sie einen konstanten Betrag (-1 oder 1) zurück wenn Sie die Typen nicht vergleichen können
  • Falls Sie sicher sind, dass Sie Euro mit Euro vergleichen, können das Signum der Differenz des Wert in Cent benutzen.

Die Schnittstelle Waehrung

Benutzen Sie dies Klasse

package s1.block11;

/**
*
* @author s@scalingbits.com
* @ version 1.1
*/
public interface Waehrung {
/**
*
* @return Währungssymbol
*/
public String symbol();
/**
* Multipliziert den Wert des Objekts mit der Fließkommazahl
*
* @param f
* @return neues Objekt welches das Produkt enthält
*/
public Waehrung mult(double f);
}

Ein Testprogramm TestEuro

package s1.block11;
import java.util.Arrays;
/**
 *
 * @author s@scalingbits.com
 * @ version 1.1
 */

public class TestEuro {
public static void main(String[] args) {
System.out.println("Phase 1: Einfache Tests");
Euro konto1 = new Euro(172, 12);
long d1 = konto1.longValue();
System.out.println(" Euro.longValue().Sollwert 172; Istwert: " + d1);
double d2 = konto1.doubleValue();
System.out.println(" Euro.doubleValue().Sollwert 172.12; Istwert: " + d2);
float d3 = konto1.floatValue();
System.out.println(" Euro.floatValue().Sollwert 172.12; Istwert: " + d3);
System.out.println(" Euro.symbol().Sollwert €; Istwert: " + konto1.symbol());
System.out.println(" Euro.toString().Sollwert 172.12€; Istwert: " + konto1);
System.out.println("Phase 2: Härtere Tests");
Euro konto2 = new Euro(-380, 25);
d1 = konto2.longValue();
System.out.println(" Euro.longValue().Sollwert -380; Istwert: " + d1);
d2 = konto2.doubleValue();
System.out.println(" Euro.doubleValue().Sollwert -380.25; Istwert: " + d2);
d3 = konto2.floatValue();
System.out.println(" Euro.floatValue().Sollwert -380.25; Istwert: " + d3);
Euro konto3 = new Euro (-382,225);
d1 = konto3.longValue();
System.out.println(" Euro.longValue().Sollwert -382; Istwert: " + d1);
d2 = konto3.doubleValue();
System.out.println(" Euro.doubleValue().Sollwert -382; Istwert: " + d2);
d3 = konto3.floatValue();
System.out.println(" Euro.floatValue().Sollwert -382; Istwert: " + d3);
System.out.println("Phase 3: Multiplikation testen");
Waehrung konto10 = new Euro(1,23);
double m1 = 2;
Waehrung konto11 = konto10.mult(m1);
System.out.println(" Euro.mult(): "+konto10 +" * "+ m1 + " = " + konto11);
Waehrung konto20 = new Euro(1,98);
double m2 = 2;
Waehrung konto21 = konto20.mult(m2);
System.out.println(" Euro.mult(): "+konto20 +" * "+ m2 + " = " + konto21);
Waehrung konto30 = new Euro(-1,98);
double m3 = 2;
Waehrung konto31 = konto30.mult(m3);
System.out.println(" Euro.mult(): "+konto30 +" * "+ m3 + " = " + konto31);
Waehrung konto40 = new Euro(-1,98);
double m4 = -2;
Waehrung konto41 = konto40.mult(m4);
System.out.println(" Euro.mult(): "+konto40 +" * "+ m4 + " = " + konto41);
Waehrung konto50 = new Euro(10,10);
double m5 = -2.01;
Waehrung konto51 = konto50.mult(m5);
System.out.println(" Euro.mult(): "+konto50 +" * "+ m5 + " = " + konto51);
Waehrung konto60 = new Euro(10,1);
double m6 = -2.001;
Waehrung konto61 = konto60.mult(m6);
System.out.println(" Euro.mult(): "+konto60 +" * "+ m6 + " = " + konto61);
System.out.println("Phase 4: Einfache Tests für Comparable");
if (konto1.compareTo(konto2) >0)
{System.out.println(" "+konto1 + " ist groeßer als " + konto2);}
else
{System.out.println(" "+konto1 + " ist nicht groeßer als " + konto2);}
if (konto3.compareTo(konto2) >0)
{System.out.println(" "+konto3 + " ist groeßer als " + konto2);}
else
{System.out.println(" "+konto3 + " ist nicht groeßer als " + konto2);}
sortierTest();
}
public static void sortierTest() {
System.out.println("Phase 4: Testen der Schnittstelle Comparable");
Euro[] bank = {
new Euro(99,99),
new Euro(66,66),
new Euro(33,33),
new Euro(11,11),
new Euro(22,22),
new Euro(88,88),
new Euro(55,55),
new Euro(22,22),
new Euro(44,44),
new Euro(0,0)};
System.out.println(" Unsortiertes Feld:");
for (Euro konto : bank) System.out.println(" "+konto);
// Diese Methode sortiert ein Feld in seiner natürlichen Ordnung
// Die ntürliche Ordnung wird durch die Schnittstelle Comparable
// festgelegt
Arrays.sort(bank);
System.out.println(" Sortiertes Feld:");
for (Euro konto : bank) System.out.println(" "+konto);
}

 

Stefan Schneider Wed, 03/30/2011 - 13:33

11.1.3 Lösung: Schnittstellen und abstrakte Klassen

11.1.3 Lösung: Schnittstellen und abstrakte Klassen

Lösung Übung 1: Klasse Dollar

package s1.block11;
import static java.lang.Math.signum;
/**
*
* @author s@scalingbits.com
* @version 1.2
*/
public class Dollar extends Number{
public final long cents;
public Dollar(int dollars, int cents) {
// Ignoriere Centsbetrag wenn er nicht im richtigen Intervall ist
if ((cents<0) || (99<cents)) cents=0;
if (dollars == 0)
this.cents = cents;
else
// Signum ist notwendig da
// -2.20= -2 - 0.2 sind. Falsch: -2 + 0.2 ergibt -1.8!
this.cents = dollars*100+cents*(long)signum(dollars);
}
@Override
public int intValue() {
return (int)cents/100;
}
@Override
public long longValue() {
return cents/100;
}
@Override
public float floatValue() {
return cents/100f;
}
@Override
public double doubleValue() {
return cents/100d;
}
}

Lösung Übung 2: Klasse Euro

package s1.block11;
import static java.lang.Math.abs;
import static java.lang.Math.signum;
/**
*
* @author s@scalingbits.com
* @version 1.2
*/
public class Euro extends Number implements Waehrung, Comparable{
/**
* Der gesamte Betrag wird intern in Cents verwaltet
*/
public final long cents;

public Euro(long euros, long cents) {
// Ignoriere Centsbetrag wenn er nicht im richtigen Intervall ist
if ((cents<0) || (99<cents)) cents=0;
if (euros == 0)
this.cents = cents;
else
// Signum ist notwendig da
// -2.20= -2 - 0.2 sind. Falsch: -2 + 0.2 ergibt -1.8!
this.cents = euros*100+cents*(long)signum(euros);
}
@Override
public int intValue() {
return (int)cents/100;
}
@Override
public long longValue() {
return cents/100;
}
@Override
public float floatValue() {
return cents/100f;
}
@Override
public double doubleValue() {
return cents/100d;
}
@Override
public String symbol() {
return "€";
}
@Override
public String toString() {
// Füge eine Null bei Centbeträgen zwischen 0 und 9 eine
String leerstelle = ((abs(cents)%100)<10) ? "0" : "";
return Long.toString(cents/100L) + "." + leerstelle +
Long.toString(abs(cents%100L)) + symbol();
}
@Override
public Waehrung mult(double d) {
long temp;
temp = (long)(cents *d);
return new Euro(temp/100L,abs(temp%100L));
}
@Override
public int compareTo(Object o) {
int result;
if (o instanceof Euro) {
Euro e = (Euro) o;
result = (int)(this.cents-e.cents);
}
else {result = -1;} // Alles was kein Euro ist, ist kleiner
return result;
}
}

 

Stefan Schneider Thu, 02/10/2011 - 10:17

11.1.4 Lernziele (Schnittstellen)

11.1.4 Lernziele (Schnittstellen)

Am Ende dieses Blocks können Sie:

  • den Zusammenhang zwischen "Information Hiding" und Java Schnittstellen erklären
  • die in Java-Schnittstellem vorkommenden Komponenten von Klassen nennen
  • die Unterschiede und Gemeinsamkeiten von Javaklassen und Javaschnittstellen erklären
  • die Syntax einer Javaschnitstelle nennen
  • die UML Notation für Schnittstellen aufzeichnen und anwenden
  • beim Entwurf von Anwendungen Gründe für die Wahl von Vererbung mit Polymorphismus oder die Wahl von Schnittstellen nennen.

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Java Schnittstellen (Interfaces)

Stefan Schneider Fri, 12/28/2012 - 19:51