Wrapperklassen: Unterschied zwischen den Versionen

Aus Programmieren-Wiki
(Die Seite wurde neu angelegt: „{{Baustelle}} Kategorie:Java Grundlagen“)
 
Keine Bearbeitungszusammenfassung
 
(5 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
{{Baustelle}}
{{CategoryBlock
[[Kategorie:Java Grundlagen]]
|Baustelle=Ja
|Java Grundlagen=Ja
|Organisation=Nein
|Programmierstil=Nein
|Bewertungsrichtlinie=Nein
|blattAnnotation=1
|blattAbzug=1
}}
{{Inhaltsblock
|vorher====Beschreibung===
Als Wrapper bezeichnet man ein Objekt, das ein anderes Objekt verpackt, um dessen Zugriff oder Verhalten zu kontrollieren. Wird in Java von Wrapperklassen gesprochen, sind damit die Klassen gemeint, die primitive [[Datentypen]] in Objekte (Referenzdatentypen) verpacken. Zu jedem der acht primitiven Datentypen stellt Java eine Wrapperklasse bereit, die ohne <syntaxhighlight inline lang="Java">import</syntaxhighlight> verwendet werden:
 
{{{!}} class="wikitable"
!Primitiver Datentyp
!Wrapperklasse
{{!}}-
{{!}}boolean {{!}}{{!}} Boolean
{{!}}-
{{!}}byte {{!}}{{!}} Byte
{{!}}-
{{!}}short {{!}}{{!}} Short
{{!}}-
{{!}}char {{!}}{{!}} Character
{{!}}-
{{!}}int {{!}}{{!}} Integer
{{!}}-
{{!}}long {{!}}{{!}} Long
{{!}}-
{{!}}float {{!}}{{!}} Float
{{!}}-
{{!}}double {{!}}{{!}} Double
{{!}}}
 
}}
{{Inhaltsblock
|vorher=Primitive Datentypen können durch eine Zuweisung in Wrapperklassen ein- und ausgepackt (''auto-boxing'' und ''auto-unboxing'') werden:
|Beispiel=Integer foo = 123; //auto-boxing
int bar = foo; //auto-unboxing
|nachher=Wie alle Referenzdatentypen können auch Instanzen von Wrapperklassen den Wert <syntaxhighlight inline lang="Java">null</syntaxhighlight> annehmen. Bei primitiven Datentypen ist das nicht möglich. Bei dem Versuch, eine <syntaxhighlight inline lang="Java">null</syntaxhighlight>-Referenz auszupacken, wird eine <syntaxhighlight inline lang="Java">NullPointerException</syntaxhighlight> geworfen.
}}
{{Inhaltsblock
|vorher====Richtige Verwendung===
}}
{{Inhaltsblock
|vorher=*; Parsen
Häufig liegen Werte von primitiven Datentypen als <syntaxhighlight inline lang="Java">String</syntaxhighlight> vor, z. B. nach Benutzereingaben. Damit diese sicher in den primitiven Datentyp umgewandelt werden können, stellt jede Wrapperklasse, außer <syntaxhighlight inline lang="Java">Character</syntaxhighlight>, eine statische <syntaxhighlight inline lang="Java">parse*()</syntaxhighlight> Methode zur Verfügung. Der ''*'' representiert dabei den primitiven Datentyp.
}}
{{Inhaltsblock
|color=red
|vorher=====Negativbeispiel====
Das folgende Programm versucht <syntaxhighlight inline lang="Java">"123"</syntaxhighlight> in einen <syntaxhighlight inline lang="Java">int</syntaxhighlight> zu überführen.
|Beispiel=char[] characters = "123".toCharArray();
int number = 0;
for (char character : characters) {
    number = number * 10 + (character - '0');
}
System.out.println(number);
|nachher=Die Lösung ist ineffizient, ignoriert einen möglichen Integer-Überlauf und berechnet einen falschen Wert, wenn <syntaxhighlight inline lang="Java">character</syntaxhighlight> keine Zahl zwischen 0-9 ist. Sie sollte daher auf keinen Fall verwendet werden.
}}
{{Inhaltsblock
|color=green
|vorher=====Positivbeispiel====
|Beispiel=int number = 0;
try {
    number = Integer.parseInt("123");
} catch (NumberFormatException e) {
    System.out.println("Error, " + e.getMessage());
}
System.out.println(number);
|nachher=Dieser Ansatz verwendet die bereitgestellte <syntaxhighlight inline lang="Java">Integer.parseInt()</syntaxhighlight>-Methode, die intern bereits alle möglichen Fehler abdeckt. Außerdem ist diese Lösung deutlich verständlicher. Die Ausnahme <syntaxhighlight inline lang="Java">NumberFormatException</syntaxhighlight> sollte immer abgefangen werden, mehr dazu [[Runtime_Exceptions|hier]].
}}
{{Inhaltsblock
|vorher=*; Primitive Datentypen vor Wrapperklassen
}}
{{Inhaltsblock
|color=red
|vorher=====Negativbeispiel====
|Beispiel=Integer[] values = new Integer[] { 1, 2, 3, 4 };
Integer sum = 0;
for (Integer value : values) {
    sum += value;
}
System.out.println(sum);
|nachher=Da das Array nur Ganzzahlen enthält ist es hier unnötig, die Wrapperklasse <syntaxhighlight inline lang="Java">Integer</syntaxhighlight> zu verwenden. Das selbe Ergebnis kann auch mit dem primitiven <syntaxhighlight inline lang="Java">int</syntaxhighlight> erzielt werden. Gleiches gilt auch für einfache Variablen
}}
{{Inhaltsblock
|color=green
|vorher=====Positivbeispiel====
|Beispiel=int[] values = new int[] { 1, 2, 3, 4 };
int sum = 0;
for (int value : values) {
    sum += value;
}
System.out.println(sum);
|nachher=Jetzt wird nur noch <syntaxhighlight inline lang="Java">int</syntaxhighlight> verwendet. Das ist performanter und verbraucht weniger Speicherplatz.
}}
{{Inhaltsblock
|vorher=*; Datenstrukturen
In generisch typparametrisierten Datenstrukturen (z. B. ''Collections'') ist es nicht möglich, primitive Datentypen zu verwenden. Hier werden Wrapperklassen benötigt.
}}
{{Inhaltsblock
|color=green
|vorher=====Positivbeispiel====
Die Methode <syntaxhighlight inline lang="Java">getData()</syntaxhighlight> gibt eine Liste von <syntaxhighlight inline lang="Java">Double</syntaxhighlight>-Werten zurück, die auch <syntaxhighlight inline lang="Java">null</syntaxhighlight> enthalten kann.
|Beispiel=List<Double> values = getData();
double highest = 0;
for (Double value : values) {
    if (value != null && value > highest) {
        highest = value;
    }
}
System.out.println(highest);
|nachher=Die Schleifenvariable <syntaxhighlight inline lang="Java">value</syntaxhighlight> muss vom Typ <syntaxhighlight inline lang="Java">Double</syntaxhighlight> sein, um die Möglichkeit von <syntaxhighlight inline lang="Java">null</syntaxhighlight> abzudecken. Die Variable <syntaxhighlight inline lang="Java">highest</syntaxhighlight> kann ein primitiver <syntaxhighlight inline lang="Java">double</syntaxhighlight> sein, da nur der numerische Wert relevant ist. Bevor <syntaxhighlight inline lang="Java">value</syntaxhighlight> zu <syntaxhighlight inline lang="Java">highest</syntaxhighlight> zugewiesen wird, wird überprüft, dass sie nicht <syntaxhighlight inline lang="Java">null</syntaxhighlight> ist.
}}
{{Inhaltsblock
|vorher=*; Vergleiche mit <syntaxhighlight inline lang="Java">equals()</syntaxhighlight>
Bei Wrapperklasseninstanzen handelt es sich nicht mehr um primitive Datentypen. Eine Überprüfung mit dem einfachen Gleichheitsoperator (<syntaxhighlight inline lang="Java">==</syntaxhighlight>) kann ein unerwartetes Ergebnis bringen. Um inhaltliche Gleichheit von zwei Objekten zu prüfen, muss die <syntaxhighlight inline lang="Java">equals()</syntaxhighlight> Methode verwenden werden.
}}
{{Inhaltsblock
|color=red
|vorher=====Negativbeispiel====
|Beispiel=Integer foo = 128;
Integer bar = 128;
System.out.println(foo == bar);
|nachher=Es wird <syntaxhighlight inline lang="Java">false</syntaxhighlight> ausgegeben, obwohl beide Instanzen den selben Wert beinhalten.
}}
{{Inhaltsblock
|color=green
|vorher=====Positivbeispiel====
|Beispiel=Integer foo = 128;
Integer bar = 128;
System.out.println(foo.equals(bar));
|nachher=Nun wird korrekt auf inhaltliche Gleichheit geprüft und es wird <syntaxhighlight inline lang="Java">true</syntaxhighlight> ausgegeben.
}}
 
{{Inhaltsblock
|color=blue
|vorher=Grundsätzlich sollten Instanzen von Wrapperklassen nur dann verwendet werden, wenn primitive Datentypen nicht möglich sind! Primitive Datentypen sind schneller und resourcenschonender.
}}
<!--##########################################################################-->
<!--
{{Inhaltsblock
|vorher====Verwendung===
Alle Wrapperklassen verfügen über die [[Statische_Methoden_und_Attribute|statische Methode]] <syntaxhighlight inline lang="Java">valueOf()</syntaxhighlight>, welche einen primitiven Datentyp in den zugehörigen Referenzdatentyp überführt. Dieser Vorgang wird ''boxing'' genannt:
|Beispiel=Integer foo = Integer.valueOf(80); //boxing
Boolean bar = Boolean.valueOf(false); //boxing
|beispielname=WrapperklassenEx1
}}
{{Inhaltsblock
|vorher=Für das „Ausgepacken“ ''(unboxing)'' gibt es die Methode <syntaxhighlight inline lang="Java">*Value()</syntaxhighlight> (der ''*'' muss mit dem Namen des primitiven Datentyps ersetzt werden):
|Beispiel=int quux = foo.intValue(); //unboxing
boolean baz = bar.booleanValue(); //unboxing
|beispielname=WrapperklassenEx2
}}
{{Inhaltsblock
|vorher=Aufgrund der besonderen Relevanz von Wrapperklassen kann der Compiler das (un)boxing übernehmen. Es genügt daher, im Quellcode das kürzere auto-(un)boxing zu verwenden. Im Bytecode selbst ist nur das (un)boxing zu finden.
|Beispiel=Integer foo = 123; //auto-boxing
Boolean bar = false; //auto-boxing
int quux = foo; //auto-unboxing
boolean baz = bar; //auto-unboxing
|beispielname=WrapperklassenEx3
}}
{{Inhaltsblock
|vorher=Ist die Instanz einer Wrapperklasse <syntaxhighlight inline lang="Java">null</syntaxhighlight>, wird beim (auto-)unboxing eine <syntaxhighlight inline lang="Java">NullPointerException</syntaxhighlight> geworfen. <br>
Zur besseren Lesbarkeit sollte im Quellcode auto-(un)boxing bevorzugt werden. <br>
Instanzen von Wrapperklassen können auch über die Konstruktoren erstellt werden. Das ist jedoch nur sehr selten sinnvoll. Sie sind zudem als veraltet markiert und sollten deshalb vermieden werden.
}}
{{Inhaltsblock
|vorher====Anwendungsbereiche===
Wrapperklassen stellen verschiedene Methoden zur Verfügung. Von [[Runtime_Exceptions|hier]] ist vielleicht die Methode <syntaxhighlight inline lang="Java">Integer.parseInt()</syntaxhighlight> bekannt, mit welcher die String-Repräsentation eines Integers in einen (primitiven) <syntaxhighlight inline lang="Java">int</syntaxhighlight>-Wert umgewandelt werden kann. Alle Wrapperklassen, außer <syntaxhighlight inline lang="Java">Character</syntaxhighlight>, bieten solch eine Methode an. <br>
Die Methode <syntaxhighlight inline lang="Java">valueOf()</syntaxhighlight>, welche bereits beim boxing angesprochen wurde, ist überladen und akzeptiert auch einen <syntaxhighlight inline lang="Java">String</syntaxhighlight> als Argument. Sie gibt allerdings eine Instanz zurück. <syntaxhighlight inline lang="Java">valueOf()</syntaxhighlight> sollte daher nur verwendet werden, wenn bewusst eine Instanz benötigt wird.
|Beispiel=try {
    int foo = Integer.parseInt("123");
    Integer bar = Integer.valueOf("123");
} catch (NumberFormatException e) {
    System.out.println("Error, string does not represent an integer.");
}
|beispielname=WrapperklassenEx4
}}
{{Inhaltsblock
|vorher=Alle numerischen Wrapperklassen (<syntaxhighlight inline lang="Java">Byte</syntaxhighlight>, <syntaxhighlight inline lang="Java">Short</syntaxhighlight>, <syntaxhighlight inline lang="Java">Integer</syntaxhighlight>, <syntaxhighlight inline lang="Java">Long</syntaxhighlight>, <syntaxhighlight inline lang="Java">Float</syntaxhighlight>, <syntaxhighlight inline lang="Java">Double</syntaxhighlight>) erben von der abstrakten Klasse <syntaxhighlight inline lang="Java">Number</syntaxhighlight>, sodass die folgenden Instanzmethoden immer vorhanden sind: <syntaxhighlight inline lang="Java">byteValue()</syntaxhighlight>, <syntaxhighlight inline lang="Java">shortValue()</syntaxhighlight>, <syntaxhighlight inline lang="Java">intValue()</syntaxhighlight>, <syntaxhighlight inline lang="Java">longValue()</syntaxhighlight>, <syntaxhighlight inline lang="Java">floatValue()</syntaxhighlight>, <syntaxhighlight inline lang="Java">doubleValue()</syntaxhighlight>. Verschiedene Zahlenformate können so einfach umgewandelt werden. Die Methoden werden unter anderem beim unboxing verwendet und beruhen auf [[Cast_außerhalb_der_equals-Methode|expliziter Typumwandlung]].
|Beispiel=Double foo = 12345.6;
int bar = foo.intValue(); //12345
|beispielname=WrapperklassenEx5
}}
{{Inhaltsblock
|vorher=Wrapperklassen legen zudem viele Grenzwerte als [[Variablen|Konstanten]] fest. <syntaxhighlight inline lang="Java">MAX_VALUE</syntaxhighlight> und <syntaxhighlight inline lang="Java">MIN_VALUE</syntaxhighlight> definieren den höchsten und niedrigsten annehmbaren Wert, Gleitkommazahlwrapperklassen decken mit <syntaxhighlight inline lang="Java">POSITIVE_INFINITY</syntaxhighlight> und <syntaxhighlight inline lang="Java">NEGATIVE_INFINITY</syntaxhighlight> positive und negative Unendlichkeit ab.
}}
{{Inhaltsblock
|vorher=Außerdem finden Wrapperklasseninstanzen häufig Verwendung in generisch typparametrisierten Strukturen. Eine generisch typparametrisierte Struktur erwartet ein Referenzobjekt. Primitive Datentypen sind keine Referenzdatentypen und können daher nicht ohne Wrapper verwendet werden:
|Beispiel=List<int> foo = new ArrayList<>(); //Compilererror
List<Integer> bar = new ArrayList<>();         //Ok
 
Map<int, boolean> quux = new HashMap<>();      //Compilererror
Map<Integer, Boolean> baz = new HashMap<>();    //Ok
|beispielname=WrapperklassenEx6
}}
{{Inhaltsblock
|vorher=Grundsätzlich sollten Wrapperklassen nur dann verwendet werden, wenn primitive Datentypen nicht möglich sind.
}}
{{Inhaltsblock
|vorher====Gleichheit von Objekten===
Da nun keine primitiven Datentypen mehr vorliegen, kann die Verwendung des einfachen Gleichheitsoperators (<syntaxhighlight inline lang="Java">==</syntaxhighlight>) zu unerwarteten Ergebnissen führen. Ein grundlegendes Verständnis der zugrunde liegenden Funktionsweise ist daher wichtig. Instanzen von Wrapperklassen verfügen nämlich in der Regel nicht über referenzielle Gleichheit. Es muss daher die <syntaxhighlight inline lang="Java">equals()</syntaxhighlight>-Methode verwendet werden.
}}
{{Inhaltsblock
|vorher=====Objekte mit dem Konstruktor====
Im Folgenden werden zwei <syntaxhighlight inline lang="Java">Integer</syntaxhighlight>-Objekte durch den Konstruktor mit <syntaxhighlight inline lang="Java">new</syntaxhighlight> erstellt. Das <syntaxhighlight inline lang="Java">new</syntaxhighlight> konstruiert immer paarweise verschiedene Objekte:
|Beispiel=Integer foo = new Integer(123);
Integer bar = new Integer(123);
System.out.println(foo == bar);  //false, unterschiedliche Referenzen und kein unboxing im Vergleich, da beide Operanden Referenzen sind
|beispielname=WrapperklassenEx7
}}
{{Inhaltsblock
|vorher=====Objekte mit boxing====
Auch durch die Verwendung von auto-boxing bzw. <syntaxhighlight inline lang="Java">valueOf()</syntaxhighlight> kann standardmäßig keine referenzielle Gleichheit gewährleistet werden:
|Beispiel=Integer foo1 = 127;
Integer foo2 = 127;
System.out.println(foo1 == foo2);     //true, selbe Referenz
 
Integer bar1 = 128;
Integer bar2 = 128;
System.out.println(bar1 == bar2); //false, unterschiedliche Referenzen
System.out.println(bar1.equals(bar2)); //true, inhaltliche Gleichheit
|beispielname=WrapperklassenEx8
}}
{{Inhaltsblock
|vorher=Häufig verwendete Werte werden von Wrapperklassen gepoolt. Das bedeutet, dass Redundanzen von gleichen und unveränderlichen Werten im Speicher vermieden werden. Im Falle von Ganzzahlen ist das mindestens das Intervall [-128, 127], bei Wahrheitswerten <syntaxhighlight inline lang="Java">true</syntaxhighlight> und <syntaxhighlight inline lang="Java">false</syntaxhighlight> und bei einzelnen Zeichen [\u0000, \u007f]. Bei allen anderen Werten kann keine Aussage über die referenzielle Gleichheit getroffen werden.
}}
{{Inhaltsblock
|vorher=====Gleichheitsvergleich von Wrapperklasse und primitivem Datentyp====
Wird ein Gleichheitsvergleich von einem primitiven Datentyp und einer Instanz der zugehörigen Wrapperklasse durchgeführt, wird die Instanz (auto-)unboxed. <br>
Im Folgenden wird der Wert von <syntaxhighlight inline lang="Java">bar2</syntaxhighlight> aus dem vorherigen Beispiel mit <syntaxhighlight inline lang="Java">1</syntaxhighlight> multipliziert, dafür muss <syntaxhighlight inline lang="Java">bar2</syntaxhighlight> zuerst in einen primitiven Datentyp auto-unboxed werden. Nun ist der linke Operand eine Instanz der Wrapperklasse des rechten primitiven Datentyps. Also wird auch <syntaxhighlight inline lang="Java">bar1</syntaxhighlight> auto-unboxed und es werden zwei primitive Datentypen verglichen.
|Beispiel=System.out.println(bar1 == bar2*1); //true
|beispielname=WrapperklassenEx9
}}
-->

Aktuelle Version vom 21. Oktober 2025, 12:41 Uhr

🚧 Diese Seite befindet sich in Bearbeitung 🚧

Beschreibung

Als Wrapper bezeichnet man ein Objekt, das ein anderes Objekt verpackt, um dessen Zugriff oder Verhalten zu kontrollieren. Wird in Java von Wrapperklassen gesprochen, sind damit die Klassen gemeint, die primitive Datentypen in Objekte (Referenzdatentypen) verpacken. Zu jedem der acht primitiven Datentypen stellt Java eine Wrapperklasse bereit, die ohne import verwendet werden:

Primitiver Datentyp Wrapperklasse
boolean Boolean
byte Byte
short Short
char Character
int Integer
long Long
float Float
double Double

Primitive Datentypen können durch eine Zuweisung in Wrapperklassen ein- und ausgepackt (auto-boxing und auto-unboxing) werden:

Integer foo = 123;	//auto-boxing
int bar = foo;		//auto-unboxing
Wie alle Referenzdatentypen können auch Instanzen von Wrapperklassen den Wert null annehmen. Bei primitiven Datentypen ist das nicht möglich. Bei dem Versuch, eine null-Referenz auszupacken, wird eine NullPointerException geworfen.

Richtige Verwendung

  • Parsen

Häufig liegen Werte von primitiven Datentypen als String vor, z. B. nach Benutzereingaben. Damit diese sicher in den primitiven Datentyp umgewandelt werden können, stellt jede Wrapperklasse, außer Character, eine statische parse*() Methode zur Verfügung. Der * representiert dabei den primitiven Datentyp.

Negativbeispiel

Das folgende Programm versucht "123" in einen int zu überführen.

char[] characters = "123".toCharArray();
int number = 0;
for (char character : characters) {
    number = number * 10 + (character - '0');
}
System.out.println(number);
Die Lösung ist ineffizient, ignoriert einen möglichen Integer-Überlauf und berechnet einen falschen Wert, wenn character keine Zahl zwischen 0-9 ist. Sie sollte daher auf keinen Fall verwendet werden.

Positivbeispiel

int number = 0;
try {
    number = Integer.parseInt("123");
} catch (NumberFormatException e) {
    System.out.println("Error, " + e.getMessage());
}
System.out.println(number);
Dieser Ansatz verwendet die bereitgestellte Integer.parseInt()-Methode, die intern bereits alle möglichen Fehler abdeckt. Außerdem ist diese Lösung deutlich verständlicher. Die Ausnahme NumberFormatException sollte immer abgefangen werden, mehr dazu hier.
  • Primitive Datentypen vor Wrapperklassen

Negativbeispiel

Integer[] values = new Integer[] { 1, 2, 3, 4 };
Integer sum = 0;
for (Integer value : values) {
    sum += value;
}
System.out.println(sum);
Da das Array nur Ganzzahlen enthält ist es hier unnötig, die Wrapperklasse Integer zu verwenden. Das selbe Ergebnis kann auch mit dem primitiven int erzielt werden. Gleiches gilt auch für einfache Variablen

Positivbeispiel

int[] values = new int[] { 1, 2, 3, 4 };
int sum = 0;
for (int value : values) {
    sum += value;
}
System.out.println(sum);
Jetzt wird nur noch int verwendet. Das ist performanter und verbraucht weniger Speicherplatz.
  • Datenstrukturen

In generisch typparametrisierten Datenstrukturen (z. B. Collections) ist es nicht möglich, primitive Datentypen zu verwenden. Hier werden Wrapperklassen benötigt.

Positivbeispiel

Die Methode getData() gibt eine Liste von Double-Werten zurück, die auch null enthalten kann.

List<Double> values = getData();
double highest = 0;
for (Double value : values) {
    if (value != null && value > highest) {
        highest = value;
    }
}
System.out.println(highest);
Die Schleifenvariable value muss vom Typ Double sein, um die Möglichkeit von null abzudecken. Die Variable highest kann ein primitiver double sein, da nur der numerische Wert relevant ist. Bevor value zu highest zugewiesen wird, wird überprüft, dass sie nicht null ist.
  • Vergleiche mit equals()

Bei Wrapperklasseninstanzen handelt es sich nicht mehr um primitive Datentypen. Eine Überprüfung mit dem einfachen Gleichheitsoperator (==) kann ein unerwartetes Ergebnis bringen. Um inhaltliche Gleichheit von zwei Objekten zu prüfen, muss die equals() Methode verwenden werden.

Negativbeispiel

Integer foo = 128;
Integer bar = 128;
System.out.println(foo == bar);
Es wird false ausgegeben, obwohl beide Instanzen den selben Wert beinhalten.

Positivbeispiel

Integer foo = 128;
Integer bar = 128;
System.out.println(foo.equals(bar));
Nun wird korrekt auf inhaltliche Gleichheit geprüft und es wird true ausgegeben.


Grundsätzlich sollten Instanzen von Wrapperklassen nur dann verwendet werden, wenn primitive Datentypen nicht möglich sind! Primitive Datentypen sind schneller und resourcenschonender.