Boxing und Unboxing in Scala
Zusammenfassung
Scala verfügt über automatisches Boxing und Unboxing. Dieser Newsletter stellt die verschiedenen Arten vor und gibt Empfehlungen zur Verwendung.
Seit einigen Tagen wohne ich in Stuttgart - und genieße es. Ich kann diesen Ort jedem sehr empfehlen, der einen der schöneren Orte Deutschlands besuchen möchte. Diese Umgebung eignet sich auch besonders, um an einem Scala-Newsletter zu arbeiten. Ich würde mich über Rückmeldungen zu dieser Ausgabe freuen. Sie können dazu ganz einfach das Kontaktformular verwenden.
Boxing
Boxing mittels implicit
Im Modul scala.Predef werden Methoden zur impliziten Typumwandlung definiert.
Zum Beispiel definiert Predef.int2Integer die automatische
Umwandlung (Boxing) von Int nach java.lang.Integer.
Dieses Boxing greift, wenn eine Instanz des Typs java.lang.Integer erwartet
wird, stattdessen jedoch eine Instanz des Typs scala.Int verwendet wird.
Boxing mittels implicit ist somit nichts weiter als eine
Anwendung der gegebenen Sprachmittel.
Die implizite Umwandlung wird für die Typen
Boolean, Byte, Short, Char,
Int, Long, Float und
Double definiert.
![]() | Anmerkung |
|---|---|
Ein Aufruf einer dieser Umwandlungsmethoden erzeugt immer eine neue
Instanz des jeweiligen Referenz-Datentyps. Im Gegensatz zu Java wird
in Scala für die Klassen
Byte, Short, Char,
Int und Long keinerlei Caching verwendet.
|
Compiler generiertes Boxing
Wird eine Operation ausgeführt, die statt einem primitiven Datentypen
eine Instanz von java.lang.Object benötigt,
fügt der Compiler eine automatische Typumwandlung ein.
Dies ist zum Beispiel der Fall, wenn eine der Methoden von java.lang.Object
aufgerufen wird (toString(), hashCode(), ...), wenn eine
Ausgabe mittels println erfolgt oder wenn der Wert an einen String angehängt
wird.
Die generierte Umwandlung erfolgt statt mit implicit Umwandlungen
durch Aufruf von Methoden der Klasse scala.runtime.BoxesRunTime.
Die Werte des Typs Boolean werden gecached. Bei den Typen
Character, Byte, Short, Integer
und Long werden nur bestimmte Werte gecached.
Es werden jedoch nicht Javas Cachemechanismen verwendet.
![]() | Anmerkung |
|---|---|
Martin Odersky hat auf der Scala-Mailingliste klargestellt, dass für Scala 2.8 geplant ist, in beiden Varianten des Boxings Javas Mechanismen zu verwenden. |
def printCount(count: Int) = println("Count: " + count)
Erläuterung. Für diese Anweisung werden zwei zusätzliche Objekte generiert:
Eine Instanz der Klasse scala.StringBuilder
für die Erzeugung der Zeichenkette und eine Instanz von java.lang.Integer,
die den Wert von count repräsentiert.
Der Integerwert wird durch Aufruf der Methode StringBuilder.append(Object)
angehängt. Der Compiler könnte durch Verwendung
der Variante StringBuilder.append(Int)
besseren Code generieren. So wird eine unnötige Instanz von
java.lang.Integer erzeugt.
Beachtet werden sollte, dass Scalas Wertetypen auch als Typ-Parameter verwendet
werden können. Die Typisierung erfolgt jedoch mit dem
entsprechenden Referenztypen von Java. Dass heisst, dass bei einer Typangabe von
scala.Int intern stattdessen java.lang.Integer verwendet wird.
scala.Int als Typ-Parameter
def testSortedSet() = {
val list: List[Int] = 1 :: 2 :: 3 :: Nil
val headAsInt: Int = list.head
val headAsInteger: java.lang.Integer = list.head
}
Erläuterung. Dieses Beispiel sieht zwar elegant aus, ist es aber nicht. Intuitiv erwartet
man hier eine Liste von scala.Int. Der Compiler generiert jedoch Code,
in dem Javas Referenz-Datentyp java.lang.Integer verwendet wird.
Für die Numerale 1,2,3 wird jeweils die Methode BoxesRunTime.boxToInteger
aufgerufen, die eine Integer-Instanz zurückgibt.
Für die Zuweisung val headAsInt: Int = list.head wird
ein Unboxing nach scala.Int durchgeführt.
Und für val headAsInteger: java.lang.Integer = list.head wird
unnötigerweise erst nach Int umgewandelt und
für die Zuweisung anschließend wieder zurück
nach java.lang.Integer.
![]() | Warnung |
|---|---|
Scala Werte-Klassen als Typ-Parameter zu verwenden verwirrt und ist ineffizient.
Besser ist es, direkt die entsprechenden Java-Referenztypen zu verwenden,
also z.B. java.lang.Integer.
|
Unboxing in Scala
Im Gegensatz zum Boxing findet in Scala kein automatisches, implizites Unboxing statt. Javas Referenz-Datentypen werden also nicht automatisch in Scalas Werte-Typen umgewandelt. Dazu sind eigene Methodendefinitionen notwendig.
java.lang.Integer
implicit def unboxInt(i: java.lang.Integer) = i.intValue
Erläuterung. Diese Methode zur impliziten Typumwandlung wird verwendet, wenn statt einem
erwarteten Typen scala.Int ein Wert vom Typ java.lang.Integer
verwendet wird.
Für die Typen java.lang.Boolean, java.lang.Character,
etc. sind entsprechende Definitionen notwendig.
Wie oben bereits erwähnt, findet bei Klassen, die mit einem primitiven Typen parametrisiert sind, eine automatische Umwandlung statt. Die Klasse scheint zwar mit primitiven Datentypen zu arbeiten, basiert intern jedoch auf Javas Referenz-Datentypen.
Empfehlungen
- In einem reinen Scalaprogramm sollten Javas Referenz-Datentypen möglichst nicht verwendet werden. Stattdessen sollten Scalas Datentypen verwendet werden.
- Wird ein primitiver Datentyp als Typ-Parameter verwendet führt dies zu ineffizientem Code. Wenn möglich, sollte der Quellcode so geändert werden, dass dies nicht mehr notwendig ist. Als schnelle, aber nicht beste Lösung kann der jeweilige Java Referenz-Datentyp verwendet werden.
- Boxing und Unboxing kann zu erhöhter Speicherbelastung und zu längerer Laufzeit führen. Werden in einem Scalaprogramm Java-Bibliotheken verwendet, sollte besonders auf Probleme durch automatisches Boxing / Unboxing geachtet werden.

![[Anmerkung]](../images/note.png)
![[Warnung]](../images/warning.png)