Teil 3

Advanced Scala – Implicit Conversions

Heiko Seeberger

Scala steht bekanntlich für Scalable Language. Das soll bedeuten, dass die Sprache so flexibel ist, dass sie leicht an unterschiedliche Anforderungen angepasst werden kann. In dieser Folge und in den kommenden werden wir uns mit den „Implicits“ beschäftigen, einer Gruppe von Features, die maßgeblich zu dieser Skalierbarkeit auf Sprachebene beitragen.

Heute stehen die Implicit Conversions auf der Speisekarte, die salopp gesagt dazu dienen, passend zu machen, was nicht passt. Wir wollen das an einem einfachen Beispiel konkretisieren:

	trait Pickable[T] {
	def pickOne: T
	}

Der parametrisierte Trait Pickable definiert die Methode pickOne, die ein „Etwas“ vom Typ T zurückgibt. Das könnte z. B. ein Char sein oder ein Int:

	object Main {
	def main(args: Array[String]) {
	printOne("Hello, world!")
	printOne(12345)
	}
	def printOne[T](pickable: Pickable[T]) {
	println(pickable.pickOne)
	}
	}

Die Methode printOne verwendet ein Pickable als Parameter. Dieser Code kompiliert natürlich (noch) nicht. Denn in der main-Methode rufen wir printOne mit einem String bzw. einem Int auf.

Also müssen wir Wrapper schreiben, die die verwendeten Typen in Pickables verpacken. Soweit ist es nichts Neues. Aber beim „klassischen Wrapper-Pattern“ müssten wir auch explizit die Wrapper als Argumente für die printOne-Aufrufe verwenden, sodass obiger Code immer noch nicht kompilieren könnte. Hier kommen nun die Implicit Conversions ins Spiel:

	object Pickable {
	implicit def toPickable(s: String) = new Pickable[Char] {
	override def pickOne = s.headOption getOrElse ' '
	}
	implicit def toPickable(i: Int) = new Pickable[Int] {
	override def pickOne = i.toString.head.toString.toInt
	}
	}

Wir definieren zwei Methoden, die einen String bzw. ein Int in ein Pickable verpacken. Durch das Schlüsselwort implicit sagen wir dem Compiler, dass er diese Methoden heranziehen soll, wenn er auf „ein Typ-Problem stößt“. Durch diesen kleinen aber feinen Kniff brauchen wir unsere Argumente für die printOne-Aufrufe nicht explizit zu verpacken, weil das nun der Compiler für uns tut.

Wem das jetzt undurchschaubar vorkommt, der liegt nicht ganz falsch. Denn Implicit Conversions sind ein sehr mächtiges Instrument, das mit Bedacht angewendet werden muss. Zwar wird der Clientcode einfacher, in unserem Fall also die printOne-Aufrufe, aber durch die zusätzliche Indirektion unter Umständen auch schwieriger nachzuvollziehen.

Allerdings lässt Scala diese Problematik erst gar nicht groß hochkochen, denn es gibt klare und strenge Regeln, wann und wie Implicit Conversions ins Spiel kommen. Die wichtigste Regel: Der Compiler sucht bei Bedarf bei den Companion Objects von Quell- und Zieltyp nach passenden Implicit Conversions. In unserem Beispiel, beim ersten Aufruf von printOne, wo wir einen String als Argument verwenden, wird also beim Companion von String und beim Companion von Pickable geschaut. Damit wird auch klar, warum wir die beiden implicit-Methoden in das Singleton ObjectPickable gepackt haben. Für Details über die Regeln sei auf die bekannte Literatur verwiesen (Odersky, Spoon, Venners; “Programming in Scala”; Kapitel 21).

Was haben nun Implicit Conversions mit einer skalierbaren Sprache zu tun? Dazu betrachten wir als Beispiel ScalaModules, eine Scala DSL (Domain Specific Language) für OSGi. Dort wird das zwar gute, aber doch oft zu low-level OSGi-API auf eine einfachere und nutzerfreundlichere Ebene gehoben, indem unter anderem dem guten alten BundleContext (ein OSGi-Interface) neue Methoden untergeschoben werden. Das sieht in der Anwendung dann zum Beispiel folgendermaßen aus:

	val greeting = new Greeting { ... }
	context createService greeting

Die Methode BundleContext.createService gibt es eigentlich gar nicht. Aber es gibt eine Implicit Conversion, die aus einem BundleContext einen RichBundleContext (eine ScalaModules-Klasse) macht. Und dieser bietet die Methode createService an. Wir können also mit Implicit Conversions bestehende Libraries „aufbohren“, wofür auch der Begriff „Pimp my Library“ bekannt geworden ist.

Wie immer befindet sich der Code für das Beispiel auf github unter http://wiki.github.com/weiglewilczek/demo-scala. Und ebenfalls wie immer sind Fragen oder Kommentare jederzeit willkommen.

Heiko Seeberger ist geschäftsführender Gesellschafter der Weigle Wilczek GmbH und verantwortlich für die technologische Strategie des Unternehmens mit den Schwerpunkten Java, Scala, OSGi, Eclipse RCP, Lift und Akka. Zudem ist er aktiver Open Source Committer, Autor zahlreicher Fachartikel und Redner auf einschlägigen Konferenzen.
Geschrieben von
Heiko Seeberger
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.