Implementierungsmuster in Java

Verwendung von Unterbereichswerttypen in Java

Raoul Naujoks, 4.12.2013

Definitionen

Werttyp bezeichnet den Typ eines Wertes. Hinweis: Ein Werttyp ist etwas anderes als eine Werteklasse. Eine Werteklasse ist ein Objekttyp und implementiert einen Werttyp.

Wertebereich bezeichnet die Menge der Werte die einem Werttyp angehören.

Der Begriff Bereich ist synonym zum Begriff der mathematischen Menge. Unterbereich ist synonym zu Teilmenge und echter Unterbereich zu echter Teilmenge.

Ein Unterbereichswerttyp U, kurz Unterbereichstyp, Werttyps W hat die Typsemantik von W. Der Wertebereich von U ist ein Unterbereich des Wertebereichs von W.

Erklärung

Unterbereiche werden häufig verwendet. So sind beispielsweise gültige Prozentwerte ein Unterbereich der reellen Zahlen, das geschlossene Intervall [0, 100]. In Java können beispielsweise Unterbereiche der Typen Integer oder Double als Prozentwertebereich verwendet werden. Zeichenfolgen die einen gültigen Variablennamen in Java darstellen sind ein Unterbereich aller Zeichenfolgen und können als Unterbereichstyp von java.lang.String definiert werden.

Verwendung in Java

1. Der informelle Unterbereichstyp

Häufig wird ein Unterbereichstyp U verwendet der nicht als Klasse implementiert wurde. Es wird statt des Unterbereichstyps der Oberbereichstyp verwendet. Im Beispiel der Prozentwerte z.B. der Typ Double. Die Verwendung als Unterbereichstyp geschieht dann oft dadurch, dass vor der Zuweisung eines Wertes geprüft wird, ob er im Wertebereich von U liegt. Diese Vorgehensweise hat entscheidende Nachteile:

2. Die Pseudo-Typannotation

In Java 7 gibt es keine Typannotationen. Jedoch können Annotationen von Feldern, Lokalvariablen, Parametern und Methoden als solche verwendet werden. Daher werden sie hier als Pseudo-Typannotationen bezeichnet. Gegenüber echten Typannotationen gibt es allerdings Einschränkungen. So sind Annotationen von generischen Typparametern in Java 7 nicht möglich.

Die Einschränkung eines Wertebereichs lässt sich durch eine Annotation deklarieren. So kann eine Annotation @Nonnull für eine Referenz deklarieren, dass sie nicht "null" sein darf.

Pseudo-Typannotationen haben folgende Vorteile:

Pseudo-Typannotationen haben folgende Nachteile:

3 Die Unterbereichswerteklasse

Die Verwendung einer Werteklasse, die nur einen erlaubten Unterbereich von Werten repräsentiert bietet fünf wesentliche Vorteile:

3.1 Die Unterbereichswerteklasse als Ableitung einer Werteklasse

Ein Unterbereichswerteklasse hat alle Eigenschaften der Oberbereichswerteklasse. Hinzu kommt die Eigenschaft, dass alle Instanzen Werte des Unterbereichs repräsentieren. Dies entspricht exakt der Semantik einer Spezialisierung bzw. Ableitung. Die abgeleitete Unterbereichswerteklasse hat den Vorteil das keine Konvertierung zur Oberbereichswerteklasse notwendig ist. Daher ist auch eine kovariante Nutzung möglich.

Anmerkung: Da ein Wert unveränderlich ist tritt im Gegensatz zu Variablen eines Typs T nicht das Problem der Invarianz auf. Nur-Lese-Operationen sind kovariant, d.h. eine Leseoperation auf T' ist auch eine auf T, wenn T' von T abgeleitet ist. Nur-Schreiboperationen sind hingegen kontravariant, d.h. eine Schreiboperation auf T ist auch eine auf T'. Insgesamt ist somit eine Variable vom Typ T bezüglich T invariant.

Die Ableitung einer Unterbereichswerteklasse von einer Werteklasse setzt voraus, dass sich diese ableiten lässt. Das ist in Java in der Regel nicht der Fall, da Klassen wie String, Integer oder Double final sind. Voraussetzung sind also ableitbare Werteklassen. Diese haben jedoch einige Nachteile:

Nachteilig kann im Einzelfall auch sein, dass Unterbereichsklassen, die vom zulässigen Wertebereich weniger oder im Gegensatz zu einer Oberklasse wie "String" nur einen konstanten Speicherbedarf haben stets den Speicherbedarf der Oberklasse beanspruchen.

3.2 Die Unterbereichs-Wrapperklasse

In diesem Fall definieren wir eine nicht abgeleitete Werteklasse die als Eigenschaft (Property) ein Wertobjekt oder einen primitiven Typ des Oberbereichs enthält. Die Wrappeklasse stellt dabei sicher, dass der enthaltene Wert im zulässigen Wertebereich ist.

Vorteile dieser Variante:

Nachteile dieser Variante:

3.3 Werteklassen über Werteinterfaces nutzen

Werteklassen per Interface zu nutzen setzt voraus, dass die Wertetypen durch Interfaces repräsentiert werden. Dies ist bei den üblichen Werttypen in Java nicht der Fall. Sie werden entweder, wie String, Integer oder Double durch Werteklassen implementiert oder wie bei java.awt.Point durch Identitätsklassen. Werttypen über Interfaces kommen damit nur für selbst definierte Werteinterfaces in Frage. Dennoch soll diese Implementierungsart nicht ganz übergangen werden.

Werteklassen per Interface zu nutzen hat zunächst die gleichen Vor- und Nachteile wie abgeleitete Werteklassen. Durch eine erhöhte Abstraktion von Interfaces sind zudem statische Optimierungen eines Compilers weniger gut möglich. Im Unterschied zu abgeleiteten Werteklassen kann eine über Interface genutzte mehreren Unterbereichstypen angehören. Vorteilhaft kann im Einzelfall auch sein, dass der Speicherbedarf einer Unterbereichsklasse kleiner sein kann als die des Oberwertebereichs, oder im Gegensatz zum Oberwertebereich konstant.

Fazit

Informelle Unterbereichstypen sollten vermieden werden. Sie sind ineffizient bezüglich der Programmierung und Wartung. Sie begünstigen Programmierfehler durch Übersehen der Kommentarhinweise und lassen sich kaum automatisch auswerten und validieren.

Pseudo-Typannotationen sind eine akzeptable Wahl bei Unterbereichstypen die keine aufwendige Wertebereichsprüfung benötigen. Insbesondere, wenn ein Unterbereichstyp nicht sehr häufig verwendet wird. Steht ein Prüf-Annotationsprozessor zur Verfügung können auch statische Typprüfungen durchgeführt werden. Letztlich wird mit Pseudo-Typannotationen aber ein zweites Typsystem neben dem Java-Typsystem aufgebaut. Dies ist bei @Nonnull für Referenzen nicht zu umgehen da Java keine programmdefinierten Referenztypen ermöglicht. Bei Objekttypen sowie primitiven Typen können Unterbereichswerteklassen verwendet werden. Lediglich in Performancerelevanten Fällen muss ggf. auf Unterbereichswerteklassen verzichtet werden.

Abgeleitete Unterbereichswerteklassen sind eine gute Wahl wenn sie eine relativ aufwendige Wertebereichsprüfung benötigen, gleichzeitig ein zu unterscheidender Verwendungstyp sind oder häufig verwendet werden. In Java sind abgeleitete Unterbereichswerteklassen oftmals nicht möglich weil Klassen wie java.lang.String final sind.

Unterbereichs-Wrapperklassen sind eine gute Wahl als Ersatz für eine abgeleitete Unterbereichswerteklasse wenn diese nicht implementierbar ist. Dies ist der Fall wenn die abzuleitende Klasse nicht ableitbar ist, wie beispielsweise die in Java finalen Klassen String und Integer. Sollen Werteklassen Unikatklassen sein, so ist eine Ableitbarkeit in der Regel nicht möglich. Die Verwendung von Unterbereichs-Wrapperklassen ist umständlicher als die von abgeleiteten Unterbereichswerteklassen und benötigt in Java zusätzliche Heap-Objekte.

Werteklassen über Interfaces anzusprechen ist für eigene Werttypen eine berechtigte Wahl. Interfaces bieten für die Implementierung von Unterbereichstypen zumindest den Vorteil, dass eine Werteklasse mehreren Unterbereichstypen angehören und final sein kann. Die Vorgehensweise ist in Sprachen wie Java bei Standardtypen wie String oder Integer jedoch nicht durchführbar, weil sie keine Interfaces sind.



Dieser Text unterliegt dem Urheberrecht. Die Weiterverbreitung, sowie die Übernahme von Passagen die über Zitate hinausgehen ist untersagt. Der Autor gestattet es ihnen, Kopien für den persönlichen privaten und persönlichen kommerziellen Gebrauch zu speichern und zu drucken.

Dieser Text kann jederzeit geändert werden, wird nicht notwendig dauerhaft gespeichert und ist somit nicht zitatfähig.


   Startseite http://www.naujoks-informatik.de        Zur Person        Impressum        Autor und Copyright 2013: Raoul Naujoks, Braunschweig    Stand 4.12.2013