• Home
  • Portfolio
  • Produkte
    • HandyBirthdays
    • BashSupport für IntelliJ®
  • Scala Newsletter
    • [Ausgabe 1] Boxing / Unboxing in Scala
    • [Ausgabe 2] Scala 101: Einführung
  • Referenzen
  • Kontakt
Joachim Ansorg IT-Services
This page in English

Boxing und Unboxing in Scala

von Joachim Ansorg (2009-04-04)

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]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.
Beispiel 1. Boxing durch implizite Typumwandlung
                        
val i: java.lang.Integer = 1;
                    

Erläuterung. Hier wird die implizite Konvertierung scala.Predef.int2Integer verwendet. Im Unterschied zu Javas Boxing wird die Integer-Instanz nicht gecached sondern es wird immer eine neue Instanz angelegt.

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]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.

Beispiel 2. Boxing durch compilergenerierten Code
                        
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.

Beispiel 3. 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]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.

Beispiel 4. Automatisches Unboxing für 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.

Kostenlos abonnieren

Tragen sie sich ein - sie erhalten dann alle Ausgaben des Scala Newsletters per E-Mail. Kostenlos!

Zusammenfassung

Scala verfügt über automatisches Boxing und Unboxing. Dieser Newsletter stellt die verschiedenen Arten vor und gibt Empfehlungen zur Verwendung.

Inhaltsverzeichnis

Boxing
Boxing mittels implicit
Compiler generiertes Boxing
Unboxing in Scala
Empfehlungen

Beispiele

1. Boxing durch implizite Typumwandlung
2. Boxing durch compilergenerierten Code
3. scala.Int als Typ-Parameter
4. Automatisches Unboxing für java.lang.Integer
  • Copyright © 2009, 2010 Joachim Ansorg IT-Services. All rights reserved.
  • Impressum