SlideShare ist ein Scribd-Unternehmen logo
1 von 3
Downloaden Sie, um offline zu lesen
Fachthema 

Tiefe statt Breite                                                             Eine Besonderheit und einen Unterschied zu Java stellen die
                                                                            Typen Null und Nothing dar. Sie stehen in der Vererbungshier-

Scala – Teil 3: Typen                                                       archie „ganz unten“ und sind Subtypen aller Referenz-Typen
                                                                            (Null) bzw. aller Typen überhaupt (Nothing). Null hat genau ei-

und Pattern Matching
                                                                            ne Instanz, nämlich die null-Referenz, und Nothing kennt über-
                                                                            haupt keine Instanz.

Heiko Seeberger, Jan Blankenhorn
                                                                            Typparameter
Scala hat derzeit kräftigen Rückenwind, denn diese Sprache ist nicht nur
einfacher, sondern auch mächtiger als Java. Ein knapper und prägnanter      Da die Grundlagen der Java-Generics auf Martin Odersky, den
Programmierstil sowie neue Möglichkeiten durch funktionale Program-         Vater von Scala, zurückgehen, ist es kein Wunder, dass auch Scala
mierung lassen Entwicklerherzen höher schlagen. Dazu noch viel Fle-         Typparameter kennt. Ganz ähnlich wie in Java können wir in Sca-
xibilität und volle Interoperabilität mit Java. Im letzten Teil haben wir   la einen generischen Typen definieren, indem wir nach dem Na-
Vererbung, Traits und funktionale Programmierung mit Scala unter die        men der Klasse oder des Traits einen oder mehrere Typparameter
Lupe genommen. Hier schließen wir diese Serie mit einem Blick auf das       angeben, jedoch nicht in spitzen, sondern in eckigen Klammern:
Typsystem sowie weitere wichtige Sprachfeatures ab.                           scala> class Cage[A](val animal: A)
                                                                              defined class Cage


Das Scala-Typsystem                                                         Diese Definition von Cage ermöglicht uns, beliebige Typen „ein-
                                                                            zusperren“. Dabei müssen wir dank Typinferenz den Wert für
    Wegen der leichtgewichtigen Syntax und der Typinferenz                  den Typparameter nicht angeben:
E   mag dem einen oder anderen bereits entfallen sein, dass
                                                                              scala> new Cage(new Duck("Donald"))
Scala eine statisch typisierte Sprache ist: Alles besitzt zum
                                                                              res0: Cage[Duck] = Cage(Donald)
Übersetzungszeitpunkt einen Typ. Wenn wir den Typ nicht
explizit angeben, dann ermittelt der Compiler diesen für uns:                 scala> new Cage(new Person("Angela"))
                                                                              res1: Cage[Person] = Cage(Angela)
   scala> val x = 1
   x: Int = 1
                                                                            Unser Beispiel setzt an dieser Stelle voraus, dass wir die Klas-
Wenn wir den Typ angeben, dann muss er auch passen:                         sen Animal und Person definiert haben sowie Duck als Ableitung
                                                                            von Animal.
   scala> val x: Int = "Fail"
                                                                              Natürlich wollen wir unseren Käfig so bauen, dass er nur
   <console>:5: error: type mismatch;
    found   : java.lang.String("Fail")                                      Tiere aufnehmen kann. Dazu können wir eine Obergrenze für
    required: Int                                                           die zulässigen Typen definieren, indem wir den begrenzenden
    val x: Int = "Fail"                                                     Typen mit vorangestelltem <: an den Typparameter anhängen:
                                                                              scala> class Cage[A <: Animal](val animal: A)
Was für Typen gibt es nun in Scala? Die wichtigsten finden wir                defined class Cage
in Abbildung 1. Beginnen wir zunächst einmal „ganz oben“:
Dort, an der Wurzel der Vererbungshierarchie, befindet sich                   scala> val donaldsCage = new Cage(new Duck("Donald"))
                                                                              donaldsCage: Cage[Duck] = Cage(Donald)
die abstrakte Klasse Any. Sie definiert eine überschaubare An-
zahl an Methoden, unter anderem equals, hashCode und toString                 scala> new Cage(new Person("Angela"))
sowie == als Äquivalent zu equals und != als dessen Negation.                 <console>:10: error: inferred type arguments [Person] do not
   Von Any leiten sich AnyVal und AnyRef ab. AnyVal ist die finale Ba-          conform to method apply's type parameter bounds [A <: Animal]
                                                                              ...
sis-Klasse für Wert-Typen wie z. B. Int, Long und Unit. Hier sehen
wir noch einmal sehr schön, dass Scala rein objektorientiert ist            Wie wir sehen, können wir unseren Cage nun nur noch mit An-
und keine primitiven Datentypen kennt. AnyRef hingegen ist die              imals parametrisieren, eine Person hingegen können wir nicht
Basis für alle Referenz-Typen, zu denen sowohl die Typen von                mehr hinter Gitter bringen.
Scala als auch die von Java gehören.                                          Wie sieht es eigentlich in Scala mit der Varianz aus, also der
                                                                            Frage, ob aus einer Vererbungsbeziehung der Typparameter ei-
                                                                            ne Vererbungsbeziehung der parametrisierten Typen entsteht?
                                                                            Zur Verdeutlichung folgendes Beispiel: Wir schreiben eine Me-
                                                                            thode operationFreedom, welche einen als Parameter übergebenen
                                                                            Cage öffnet und das eingesperrte Tier in die Freiheit entlässt:

                                                                              scala> def operationFreedom(cage: Cage[Animal]) {
                                                                                |   println(cage.animal + " is free now!")
                                                                                | }
                                                                              operationFreedom: (cage: Cage[Animal])Unit


                                                                            Preisfrage: Was passiert nun, wenn wir unseren zuletzt erzeug-
                                                                            ten donaldsCage als Parameter übergeben?
                                                                              scala> operationFreedom(donaldsCage)
                                                                              <console>:11: error: type mismatch;
                                                                               found   : Cage[Duck]
Abb. 1: Scala-Typsystem                                                        required: Cage[Animal]
                                                                               ...



 38                                                                                                                           JavaSPEKTRUM 4/2010
           Fachthema

Oha: Ein Cage[Duck] ist offenbar kein Cage[Animal]! Obwohl ein Duck             Ein ganz spezieller und immer vorhandener impliziter Wert
natürlich ein Animal ist. Das heißt, dass in Scala parametrisier-            ist das Manifest, welches die Information über einen Typpara-
te Typen zunächst einmal invariant sind. Das ist in der Regel                meter über die Methode erasure zur Verfügung stellt. Damit
auch gut so, denn Ko- und Kontravarianz sind kniffelige The-                 können wir obige create-Methode folgendermaßen schreiben:
men. Zum Beispiel dürfte ein veränderlicher Cage[Duck], d. h. ei-              scala> def create[T](implicit mf: Manifest[T]) =
ner mit einem Setter für die Ente, nicht als Cage[Animal] interpre-              |   mf.erasure.newInstance
tiert werden, denn er kann ja nur Ducks aufnehmen und keine –                  create: [T](implicit mf: Manifest[T])Any
sagen wir – Elefanten.
   Zum Glück gibt es jedoch in Scala die Möglichkeit, die Va-                Da wir den impliziten Parameter beim Aufruf gar nicht überge-
rianz zu beeinflussen. In unserem Fall, wo wir einen unverän-                ben müssen, ist die Verwendung dieser Methode denkbar ein-
derlichen Cage haben, möchten wir schon, dass ein Cage[Duck] ein             fach:
Cage[Animal] ist, d. h. wir wollen Kovarianz für Cage erreichen.
                                                                               scala> create[java.util.Date]
Dazu stellen wir einfach ein + vor den Typparameter:                           res5: Any = Wed May 19 08:03:12 CEST 2010

   scala> class Cage[+A <: Animal](val animal: A)
   defined class Cage                                                        Auf diese Weise können wir also die Type Erasure umgehen
                                                                             und dadurch an manchen Stellen ein einfacheres und besse-
Wenn wir nun donaldsCage und              operationFreedom   neu erzeugen,   res API schreiben. In Java hätten wir nämlich für die create-Me-
dann sind wir am Ziel:                                                       thode einen Parameter vom Typ Class benötigt. Anwendungs-
   scala> operationFreedom(donaldsCage)
                                                                             beispiele für dieses Feature sind alle Tools, die für uns Instan-
   Donald is free now!                                                       zen aufgrund von Meta-Informationen erzeugen sollen, z. B.
                                                                             Dependency-Injection-Frameworks oder OR-Mapper.
Doch wie geht Scala mit oben beschriebenem Problem um?
Ganz einfach: Würden wir unseren Cage veränderlich machen,
indem wir aus dem val ein var machen, dann bekommen wir ei-                  Case Classes und Pattern Matching
nen Compiler-Fehler:
                                                                             Abschließend gehen wir noch auf ein besonders charakteristi-
   scala> class Cage[+A <: Animal](var animal: A)
                                                                             sches Scala-Feature ein, das eigentlich aus zwei Teilen besteht.
   <console>:6: error: covariant type A occurs in contravariant position
     in type A of parameter of setter animal_=                               Zunächst einmal können wir durch Voranstellen des Schlüs-
          class Cage[+A <: Animal](var animal: A)                            selwortes case eine Klasse zur sogenannten Case Class machen:
                                                                               scala> case class Cage[+A <: Animal](animal: A)
Auf diese Weise stellt Scala schon durch das Typsystem und                     defined class Cage
den Compiler sicher, dass wir in Sachen Varianz keine Fehler
machen können. Es gäbe noch so einiges über Typparameter zu                  Dem aufmerksamen Leser wird auffallen, dass wir nun das
erzählen, z. B. Kontravarianz und Untergrenzen, aber das wür-                Schlüsselwort val vor dem Klassenparameter animal weglassen.
de den Rahmen hier sprengen. Abschließend sei erwähnt, dass                  Das liegt daran, dass der Compiler für Case Classes die Klas-
Typparameter in Scala zwar durchaus eine komplizierte Sache                  senparameter automatisch zu vals macht. Aber das ist nicht das
sind. Aber das gilt nur für diejenigen von uns, die parametri-               einzige Geschenk: Zusätzlich bekommen wir „sinnvolle“ Im-
sierte Typen schreiben. Die Nutzung hingegen ist sehr einfach                plementierungen von equals, hashCode und toString, allesamt auf
und sicher.                                                                  Basis der Klassenparameter implementiert. Darüber hinaus be-
                                                                             kommen wir auch noch automatisch ein Companion Object,
                                                                             welches die apply-Methode implementiert, um eine Case Class
Die Type Erasure umgehen                                                     ohne das Schlüsselwort new zu erzeugen. Das Ganze sieht dann
                                                                             in der Anwendung so aus:
Ergänzend zum Thema Typparameter wollen wir nun ein
                                                                               scala> val donaldsCage = Cage(new Duck("Donald"))
wirklich innovatives Scala-Feature vorstellen. Wie in Java wer-
                                                                               donaldsCage: Cage[Duck] = Cage(Donald)
den die Argumente, die im Quellcode für einen Typparameter
verwendet werden, vom Compiler entfernt. Dieses Verhalten                      scala> donaldsCage.animal
wird Type Erasure genannt und bewirkt, dass zur Laufzeit keine                 res6: Duck = Donald

Informationen über die Typparameter vorliegen. Zum Beispiel
können wir nicht ohne Weiteres ermitteln, mit welchem Typ ei-                Wir sehen, dass wir unseren Cage nun ohne new erzeugen kön-
ne einfache Methode parametrisiert ist:                                      nen, die toString-Methode den Klassennamen und in Klam-
   scala> def create[T] = T.getClass.newInstance
                                                                             mern die toString-Aufrufe der Klassenparameter zurück gibt
   <console>:5: error: not found: value T                                    und der Zugriff auf animal möglich ist, obwohl wir diesen Klas-
          def create[T] = T.getClass.newInstance                             senparameter nicht explizit zum val gemacht haben.
                                                                               Für sich alleine wären Case Classes zwar eine nette Erleich-
Scala kennt zunächst das Konzept von impliziten Parametern.                  terung für mache Anwendungsfälle, aber so richtig zur Entfal-
Wenn wir einer Parameterliste das Schlüsselwort implicit vo-                 tung kommen sie in Verbindung mit einem weiteren Sprach-
ranstellen, dann müssen wir für diese Parameter nicht expli-                 Feature, dem Pattern Matching (musterbasierter Vergleich).
zit Argumente übergeben, sofern der Compiler passende Werte                    Zunächst einmal könnte man Pattern Matching als „richtig
findet, die er verwenden kann. Passend bedeutet in diesem Fall               gemachte“ switch-Anweisung betrachten. Die offensichtlichen
nicht nur, dass die Typen passen müssen, sondern die Werte                   Unterschiede sind, dass wir beliebige Ausdrücke übergeben
müssen wiederum mit implicit definiert worden sein. Auf diese                dürfen, dass es keinen Fall-Through gibt, d. h. nach einem er-
Weise könnte eine Bibliothek zum Beispiel „vernünftige“ Vor-                 folgreichen Matching ist Schluss, und dass Pattern Matching
einstellungen zur Verfügung stellen, die wir bei Bedarf expli-               – ganz im Sinne der funktionalen Programmierung – immer
zit überschreiben.                                                           ein Resultat ergibt.

www.javaspektrum.de                                                                                                                       39
Fachthema 

  Die Syntax ist denkbar einfach: Wir schreiben einen Aus-            äußeren Schalen beginnen. So können wir nach und nach im-
druck, gefolgt vom Schlüsselwort match und anschließend einen         mer mehr Scala einführen, ohne allzu große Risiken einzuge-
Block mit case-Mustern:                                               hen und ohne mit einem Schlag alles für alle Projektbeteiligten
   scala> def whatIsIt(x: Any) = x match {
                                                                      umstellen zu müssen.
     | case "Constant" => "It's a string constant."
     | case s: String => "It's the string " + s
     | case Cage(animal) => "It's a cage with a " + animal            Fazit
     | case _ => "It's something else."
     | }
   whatIsIt: (x: Any)java.lang.String                                 Es gäbe noch einiges mehr über Scala zu berichten, insbeson-
                                                                      dere über Standardbibliotheken wie z. B. die Actors-Biblio-
   scala> whatIsIt("Hello")                                           thek. Aber was die Sprache selbst angeht, so haben wir im Rah-
   res7: java.lang.String = It's the string Hello
                                                                      men dieser dreiteiligen Serie die meisten Aspekte behandelt.
   scala> whatIsIt(donaldsCage)
                                                                      Das war möglich, weil Scala keine „breite“ Sprache ist, sondern
   res8: java.lang.String = It's a cage with a Donald                 eine „tiefe“: Die Sprache selbst ist recht schlank, wenngleich
                                                                      manche Features durchaus einen gewissen Tiefgang haben, z.
Hier ist der Ausdruck der Parameter x der Methode whatIsIt. Al-       B. das Typsystem.
le case-Muster geben einen String zurück, sodass der Rückgabe-          Dennoch ist es nicht erforderlich, gleich ganz abzutauchen
typ der Methode ein String ist. Nun zu den einzelnen Mustern:         und alles im Detail zu verstehen. Vielmehr ist es sogar dank
 Wir prüfen zunächst mit einem Constant-Muster, ob unser             der Schlankheit recht schnell möglich, Schwimmen zu lernen,
   Ausdruck eine Konstante darstellt.                                 d. h. die Sprache so zu beherrschen, dass man schon etliche
 Danach nutzen wir ein Variable-Muster mit Angabe des                Vorteile im Vergleich zu Java nutzen kann. Man denke nur an
   Typs, um zu prüfen, ob ein String vorliegt. Dabei definieren       die wesentlich kompaktere Notation und die Mächtigkeit von
   wir die Variable s, die wir auf der „rechten Seite“ des case-      funktionalen Collections.
   Musters verwenden können.                                            Wir hoffen, mit dieser Serie nicht nur Wissen über Scala ver-
 Das dritte case-Muster schließlich nutzt unsere Case Class          mittelt, sondern hoffentlich auch Lust auf Scala gemacht zu ha-
   Cage. Wir können hier einfach den Konstruktor der Case Class       ben und freuen uns sehr auf Fragen oder Feedback.
   mit Variablen für seine Argumente hinschreiben und diese
   Variablen dann auf der „rechten Seite“ verwenden. Auf die-
   se Weise sparen wir uns eine explizite Prüfung auf den Typ         Literatur und Links
   (instanceOf) sowie die Dekomposition in die Werte.
 Abschließend verwenden wir das Wildcard-Muster, um si-              [OdSpVe08] M. Odersky, L. Spoon, B. Venners, Programming in
   cherzustellen, dass wir alle möglichen Fälle abdecken, denn        Scala: A comprehensive step-by-step guide, artima 2008
   ansonsten könnte zur Laufzeit ein MatchError auftreten.            [Scala] The Scala Programming Language,
Der Vollständigkeit halber sei erwähnt, dass Pattern Matching         http://www.scala-lang.org/
nicht nur mit Case Classes so elegant funktioniert. Das wäre          [ScalaTest] Open-Source-Test-Framework,
auch schade, denn wir wollen nicht alles zu Case Classes ma-          http://www.scalatest.org/
chen und können das bei vorhandenen Klassen, z. B. aus einer          [SeeBla10a] H. Seeberger, J. Blankenhorn, Scala: – Teil 1:
Bibliothek, auch gar nicht. Es reicht vollkommen, dass wir ei-        Einstieg, in: JavaSPEKTRUM, 2/2010
nen Extractor definieren: Ein Singleton Object mit einer unapply-     [SeeBla10b] H. Seeberger, J. Blankenhorn, Scala: – Teil 2:
Methode, welche die Dekomposition durchführt.                         FP und OOP, in: JavaSPEKTRUM, 3/2010
                                                                      [Specs] A BDD Library for Scala, Project Hosting on Google Code,
                                                                      http://code.google.com/p/specs/
Scala und Java

Zum Abschluss möchten wir noch kurz beleuchten, wie wir
Scala in unsere Java-Projekte einbauen können. Das ist natür-
lich möglich, denn Scala kompiliert zu Java-Bytecode und ist
sogar hundertprozentig „abwärtskompatibel“ zu Java, sodass
wir allen vorhandenen Java-Code verwenden können. Der um-                               Heiko Seeberger ist geschäftsführender Gesell-
                                                                                        schafter der Weigle Wilczek GmbH und verantwortlich
gekehrte Weg, d. h. Scala aus Java heraus zu nutzen, ist auch
                                                                                        für die technologische Strategie des Unternehmens
möglich, allerdings müssen wir uns dabei beim API auf dieje-
                                                                                        mit den Schwerpunkten Java, Scala, OSGi, Eclipse RCP
nigen Features beschränken, die Java kennt.                                             und Lift. Zudem ist er aktiver Open Source Committer,
   Daher ist der folgende Weg vermutlich der einfachste: Wir                            Autor zahlreicher Fachartikel und Redner auf einschlä-
beginnen damit, unsere Tests nicht mehr mit JUnit oder TestNG                           gigen Konferenzen.
zu schreiben, sondern mit [Specs] oder [ScalaTest]. Das sind                            E-Mail: seeberger@weiglewilczek.com
zwei Scala-Frameworks, die uns ermöglichen, sehr ausdrucks-
                                                                                        Jan Blankenhorn ist Softwareentwickler bei der
starke Testfälle zu schreiben, bei denen tatsächlich gilt, dass der                     Weigle Wilczek GmbH. Er entwickelt sowohl dyna-
Code der Kommentar ist. Dabei sammeln wir wertvolle Praxis-                             mische Ajax-Webanwendungen als auch Rich Clients
erfahrung mit Scala und können nach einer Weile dann dazu                               mit Eclipse RCP. Neben Softwareentwicklungs- und
übergehen, einzelne Teilprojekte in Scala zu schreiben.                                 -wartungsprojekten ist er als Trainer für Eclipse RCP im
   Dabei wäre es geschickt, wenn wir solche Teilprojekte identi-                        Rahmen der Eclipse Training Alliance aktiv.
fizieren könnten, auf welche andere Teilprojekten keine Abhän-                          E-Mail: blankenhorn@weiglewilczek.com
gigkeit haben. Oder mit anderen Worten: Wenn unser Projekt
eine Zwiebel ist, bei der die äußeren Schalen von den inneren
abhängen, aber nicht umgekehrt, dann sollten wir bei den ganz

 40                                                                                                                   JavaSPEKTRUM 4/2010

Weitere ähnliche Inhalte

Andere mochten auch

2nd Life Slides
2nd Life Slides2nd Life Slides
2nd Life Slidesmacca2038
 
Para no quejarse....nunca mas
Para no quejarse....nunca masPara no quejarse....nunca mas
Para no quejarse....nunca masmedrano77
 
II Convención de Agronegocios - UPC
II Convención de Agronegocios - UPCII Convención de Agronegocios - UPC
II Convención de Agronegocios - UPCFertilMundo
 
LOCDM 18.695
LOCDM 18.695LOCDM 18.695
LOCDM 18.695CIPDEL
 
Open Data & Open Government - Information als Basis für Beteiligung
Open Data & Open Government - Information als Basis für BeteiligungOpen Data & Open Government - Information als Basis für Beteiligung
Open Data & Open Government - Information als Basis für BeteiligungLandesjugendring Niedersachsen e.V.
 
Elementos estructurales y coyunturales que motivan la aparición
Elementos estructurales y coyunturales que motivan la apariciónElementos estructurales y coyunturales que motivan la aparición
Elementos estructurales y coyunturales que motivan la apariciónereinoso43
 
Cifras ambientales 2010
Cifras ambientales 2010Cifras ambientales 2010
Cifras ambientales 2010FertilMundo
 
Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009
Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009
Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009ladstaetter
 
Die Vergangenheit Perfekt 1
Die Vergangenheit Perfekt 1Die Vergangenheit Perfekt 1
Die Vergangenheit Perfekt 1s ghani
 

Andere mochten auch (20)

Funciones y Tiipos de evaluacion
Funciones y Tiipos de evaluacionFunciones y Tiipos de evaluacion
Funciones y Tiipos de evaluacion
 
2nd Life Slides
2nd Life Slides2nd Life Slides
2nd Life Slides
 
Para no quejarse....nunca mas
Para no quejarse....nunca masPara no quejarse....nunca mas
Para no quejarse....nunca mas
 
Descripción perfil del suelo
Descripción perfil del sueloDescripción perfil del suelo
Descripción perfil del suelo
 
II Convención de Agronegocios - UPC
II Convención de Agronegocios - UPCII Convención de Agronegocios - UPC
II Convención de Agronegocios - UPC
 
LOCDM 18.695
LOCDM 18.695LOCDM 18.695
LOCDM 18.695
 
Open Data & Open Government - Information als Basis für Beteiligung
Open Data & Open Government - Information als Basis für BeteiligungOpen Data & Open Government - Information als Basis für Beteiligung
Open Data & Open Government - Information als Basis für Beteiligung
 
Pint’Arte
Pint’ArtePint’Arte
Pint’Arte
 
Presentacion final
Presentacion finalPresentacion final
Presentacion final
 
Grasas protegidas
Grasas protegidasGrasas protegidas
Grasas protegidas
 
Práctica 24
Práctica 24Práctica 24
Práctica 24
 
3275 Checkweigher
3275 Checkweigher3275 Checkweigher
3275 Checkweigher
 
Multimediales Arbeiten
Multimediales ArbeitenMultimediales Arbeiten
Multimediales Arbeiten
 
Elementos estructurales y coyunturales que motivan la aparición
Elementos estructurales y coyunturales que motivan la apariciónElementos estructurales y coyunturales que motivan la aparición
Elementos estructurales y coyunturales que motivan la aparición
 
Cifras ambientales 2010
Cifras ambientales 2010Cifras ambientales 2010
Cifras ambientales 2010
 
Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009
Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009
Spam-Abwehr beim Accessibility-Stammtisch am 12. März 2009
 
East Dallas
East DallasEast Dallas
East Dallas
 
Die Vergangenheit Perfekt 1
Die Vergangenheit Perfekt 1Die Vergangenheit Perfekt 1
Die Vergangenheit Perfekt 1
 
Sm
SmSm
Sm
 
Swproxy(2)
Swproxy(2)Swproxy(2)
Swproxy(2)
 

Mehr von Heiko Seeberger

Scaladays 2011 - The Ease of Scalaz
Scaladays 2011 - The Ease of ScalazScaladays 2011 - The Ease of Scalaz
Scaladays 2011 - The Ease of ScalazHeiko Seeberger
 
RheinJUG 2010 - Sprechen Sie Scala?
RheinJUG 2010 - Sprechen Sie Scala?RheinJUG 2010 - Sprechen Sie Scala?
RheinJUG 2010 - Sprechen Sie Scala?Heiko Seeberger
 
Objektforum 2010 - Sprechen Sie Scala?
Objektforum 2010 - Sprechen Sie Scala?Objektforum 2010 - Sprechen Sie Scala?
Objektforum 2010 - Sprechen Sie Scala?Heiko Seeberger
 
JM 08/09 - Beginning Scala Review
JM 08/09 - Beginning Scala ReviewJM 08/09 - Beginning Scala Review
JM 08/09 - Beginning Scala ReviewHeiko Seeberger
 
OSGi DevCon Europe 09 - OSGi on Scala
OSGi DevCon Europe 09 - OSGi on ScalaOSGi DevCon Europe 09 - OSGi on Scala
OSGi DevCon Europe 09 - OSGi on ScalaHeiko Seeberger
 
JAX 09 - OSGi Service Components Models
JAX 09 - OSGi Service Components ModelsJAX 09 - OSGi Service Components Models
JAX 09 - OSGi Service Components ModelsHeiko Seeberger
 
Eclipse Magazin 12 - Design by Contract
Eclipse Magazin 12 - Design by ContractEclipse Magazin 12 - Design by Contract
Eclipse Magazin 12 - Design by ContractHeiko Seeberger
 
Eclipse Magazin 16 - Die Stärke der Drei
Eclipse Magazin 16 - Die Stärke der DreiEclipse Magazin 16 - Die Stärke der Drei
Eclipse Magazin 16 - Die Stärke der DreiHeiko Seeberger
 
Eclipse Magazin15 - Performance Logging
Eclipse Magazin15 - Performance LoggingEclipse Magazin15 - Performance Logging
Eclipse Magazin15 - Performance LoggingHeiko Seeberger
 
Eclipse Magazin 14 - Getting hooked on Equinox
Eclipse Magazin 14 - Getting hooked on EquinoxEclipse Magazin 14 - Getting hooked on Equinox
Eclipse Magazin 14 - Getting hooked on EquinoxHeiko Seeberger
 
Eclipse Magazin 12 - Security does matter
Eclipse Magazin 12 - Security does matterEclipse Magazin 12 - Security does matter
Eclipse Magazin 12 - Security does matterHeiko Seeberger
 
EclipseCon 08 - Agile RCP
EclipseCon 08 - Agile RCPEclipseCon 08 - Agile RCP
EclipseCon 08 - Agile RCPHeiko Seeberger
 

Mehr von Heiko Seeberger (20)

Scaladays 2011 - The Ease of Scalaz
Scaladays 2011 - The Ease of ScalazScaladays 2011 - The Ease of Scalaz
Scaladays 2011 - The Ease of Scalaz
 
Java Magazin - Lift
Java Magazin - LiftJava Magazin - Lift
Java Magazin - Lift
 
JavaSPEKTRUM - Scala 1
JavaSPEKTRUM - Scala 1JavaSPEKTRUM - Scala 1
JavaSPEKTRUM - Scala 1
 
RheinJUG 2010 - Sprechen Sie Scala?
RheinJUG 2010 - Sprechen Sie Scala?RheinJUG 2010 - Sprechen Sie Scala?
RheinJUG 2010 - Sprechen Sie Scala?
 
Objektforum 2010 - Sprechen Sie Scala?
Objektforum 2010 - Sprechen Sie Scala?Objektforum 2010 - Sprechen Sie Scala?
Objektforum 2010 - Sprechen Sie Scala?
 
W-JAX 09 - ScalaModules
W-JAX 09 - ScalaModulesW-JAX 09 - ScalaModules
W-JAX 09 - ScalaModules
 
W-JAX 09 - Lift
W-JAX 09 - LiftW-JAX 09 - Lift
W-JAX 09 - Lift
 
JM 08/09 - Beginning Scala Review
JM 08/09 - Beginning Scala ReviewJM 08/09 - Beginning Scala Review
JM 08/09 - Beginning Scala Review
 
JM 08/09 - ScalaModules
JM 08/09 - ScalaModulesJM 08/09 - ScalaModules
JM 08/09 - ScalaModules
 
OSGi DevCon Europe 09 - OSGi on Scala
OSGi DevCon Europe 09 - OSGi on ScalaOSGi DevCon Europe 09 - OSGi on Scala
OSGi DevCon Europe 09 - OSGi on Scala
 
JAX 09 - OSGi on Scala
JAX 09 - OSGi on ScalaJAX 09 - OSGi on Scala
JAX 09 - OSGi on Scala
 
JAX 09 - OSGi Service Components Models
JAX 09 - OSGi Service Components ModelsJAX 09 - OSGi Service Components Models
JAX 09 - OSGi Service Components Models
 
JAX 08 - Agile RCP
JAX 08 - Agile RCPJAX 08 - Agile RCP
JAX 08 - Agile RCP
 
Eclipse Magazin 12 - Design by Contract
Eclipse Magazin 12 - Design by ContractEclipse Magazin 12 - Design by Contract
Eclipse Magazin 12 - Design by Contract
 
JUGM 07 - AspectJ
JUGM 07 - AspectJJUGM 07 - AspectJ
JUGM 07 - AspectJ
 
Eclipse Magazin 16 - Die Stärke der Drei
Eclipse Magazin 16 - Die Stärke der DreiEclipse Magazin 16 - Die Stärke der Drei
Eclipse Magazin 16 - Die Stärke der Drei
 
Eclipse Magazin15 - Performance Logging
Eclipse Magazin15 - Performance LoggingEclipse Magazin15 - Performance Logging
Eclipse Magazin15 - Performance Logging
 
Eclipse Magazin 14 - Getting hooked on Equinox
Eclipse Magazin 14 - Getting hooked on EquinoxEclipse Magazin 14 - Getting hooked on Equinox
Eclipse Magazin 14 - Getting hooked on Equinox
 
Eclipse Magazin 12 - Security does matter
Eclipse Magazin 12 - Security does matterEclipse Magazin 12 - Security does matter
Eclipse Magazin 12 - Security does matter
 
EclipseCon 08 - Agile RCP
EclipseCon 08 - Agile RCPEclipseCon 08 - Agile RCP
EclipseCon 08 - Agile RCP
 

JavaSPEKTRUM - Scala 3

  • 1. Fachthema  Tiefe statt Breite Eine Besonderheit und einen Unterschied zu Java stellen die Typen Null und Nothing dar. Sie stehen in der Vererbungshier- Scala – Teil 3: Typen archie „ganz unten“ und sind Subtypen aller Referenz-Typen (Null) bzw. aller Typen überhaupt (Nothing). Null hat genau ei- und Pattern Matching ne Instanz, nämlich die null-Referenz, und Nothing kennt über- haupt keine Instanz. Heiko Seeberger, Jan Blankenhorn Typparameter Scala hat derzeit kräftigen Rückenwind, denn diese Sprache ist nicht nur einfacher, sondern auch mächtiger als Java. Ein knapper und prägnanter Da die Grundlagen der Java-Generics auf Martin Odersky, den Programmierstil sowie neue Möglichkeiten durch funktionale Program- Vater von Scala, zurückgehen, ist es kein Wunder, dass auch Scala mierung lassen Entwicklerherzen höher schlagen. Dazu noch viel Fle- Typparameter kennt. Ganz ähnlich wie in Java können wir in Sca- xibilität und volle Interoperabilität mit Java. Im letzten Teil haben wir la einen generischen Typen definieren, indem wir nach dem Na- Vererbung, Traits und funktionale Programmierung mit Scala unter die men der Klasse oder des Traits einen oder mehrere Typparameter Lupe genommen. Hier schließen wir diese Serie mit einem Blick auf das angeben, jedoch nicht in spitzen, sondern in eckigen Klammern: Typsystem sowie weitere wichtige Sprachfeatures ab. scala> class Cage[A](val animal: A) defined class Cage Das Scala-Typsystem Diese Definition von Cage ermöglicht uns, beliebige Typen „ein- zusperren“. Dabei müssen wir dank Typinferenz den Wert für Wegen der leichtgewichtigen Syntax und der Typinferenz den Typparameter nicht angeben: E mag dem einen oder anderen bereits entfallen sein, dass scala> new Cage(new Duck("Donald")) Scala eine statisch typisierte Sprache ist: Alles besitzt zum res0: Cage[Duck] = Cage(Donald) Übersetzungszeitpunkt einen Typ. Wenn wir den Typ nicht explizit angeben, dann ermittelt der Compiler diesen für uns: scala> new Cage(new Person("Angela")) res1: Cage[Person] = Cage(Angela) scala> val x = 1 x: Int = 1 Unser Beispiel setzt an dieser Stelle voraus, dass wir die Klas- Wenn wir den Typ angeben, dann muss er auch passen: sen Animal und Person definiert haben sowie Duck als Ableitung von Animal. scala> val x: Int = "Fail" Natürlich wollen wir unseren Käfig so bauen, dass er nur <console>:5: error: type mismatch; found : java.lang.String("Fail") Tiere aufnehmen kann. Dazu können wir eine Obergrenze für required: Int die zulässigen Typen definieren, indem wir den begrenzenden val x: Int = "Fail" Typen mit vorangestelltem <: an den Typparameter anhängen: scala> class Cage[A <: Animal](val animal: A) Was für Typen gibt es nun in Scala? Die wichtigsten finden wir defined class Cage in Abbildung 1. Beginnen wir zunächst einmal „ganz oben“: Dort, an der Wurzel der Vererbungshierarchie, befindet sich scala> val donaldsCage = new Cage(new Duck("Donald")) donaldsCage: Cage[Duck] = Cage(Donald) die abstrakte Klasse Any. Sie definiert eine überschaubare An- zahl an Methoden, unter anderem equals, hashCode und toString scala> new Cage(new Person("Angela")) sowie == als Äquivalent zu equals und != als dessen Negation. <console>:10: error: inferred type arguments [Person] do not Von Any leiten sich AnyVal und AnyRef ab. AnyVal ist die finale Ba- conform to method apply's type parameter bounds [A <: Animal] ... sis-Klasse für Wert-Typen wie z. B. Int, Long und Unit. Hier sehen wir noch einmal sehr schön, dass Scala rein objektorientiert ist Wie wir sehen, können wir unseren Cage nun nur noch mit An- und keine primitiven Datentypen kennt. AnyRef hingegen ist die imals parametrisieren, eine Person hingegen können wir nicht Basis für alle Referenz-Typen, zu denen sowohl die Typen von mehr hinter Gitter bringen. Scala als auch die von Java gehören. Wie sieht es eigentlich in Scala mit der Varianz aus, also der Frage, ob aus einer Vererbungsbeziehung der Typparameter ei- ne Vererbungsbeziehung der parametrisierten Typen entsteht? Zur Verdeutlichung folgendes Beispiel: Wir schreiben eine Me- thode operationFreedom, welche einen als Parameter übergebenen Cage öffnet und das eingesperrte Tier in die Freiheit entlässt: scala> def operationFreedom(cage: Cage[Animal]) { | println(cage.animal + " is free now!") | } operationFreedom: (cage: Cage[Animal])Unit Preisfrage: Was passiert nun, wenn wir unseren zuletzt erzeug- ten donaldsCage als Parameter übergeben? scala> operationFreedom(donaldsCage) <console>:11: error: type mismatch; found : Cage[Duck] Abb. 1: Scala-Typsystem required: Cage[Animal] ... 38 JavaSPEKTRUM 4/2010
  • 2. Fachthema Oha: Ein Cage[Duck] ist offenbar kein Cage[Animal]! Obwohl ein Duck Ein ganz spezieller und immer vorhandener impliziter Wert natürlich ein Animal ist. Das heißt, dass in Scala parametrisier- ist das Manifest, welches die Information über einen Typpara- te Typen zunächst einmal invariant sind. Das ist in der Regel meter über die Methode erasure zur Verfügung stellt. Damit auch gut so, denn Ko- und Kontravarianz sind kniffelige The- können wir obige create-Methode folgendermaßen schreiben: men. Zum Beispiel dürfte ein veränderlicher Cage[Duck], d. h. ei- scala> def create[T](implicit mf: Manifest[T]) = ner mit einem Setter für die Ente, nicht als Cage[Animal] interpre- | mf.erasure.newInstance tiert werden, denn er kann ja nur Ducks aufnehmen und keine – create: [T](implicit mf: Manifest[T])Any sagen wir – Elefanten. Zum Glück gibt es jedoch in Scala die Möglichkeit, die Va- Da wir den impliziten Parameter beim Aufruf gar nicht überge- rianz zu beeinflussen. In unserem Fall, wo wir einen unverän- ben müssen, ist die Verwendung dieser Methode denkbar ein- derlichen Cage haben, möchten wir schon, dass ein Cage[Duck] ein fach: Cage[Animal] ist, d. h. wir wollen Kovarianz für Cage erreichen. scala> create[java.util.Date] Dazu stellen wir einfach ein + vor den Typparameter: res5: Any = Wed May 19 08:03:12 CEST 2010 scala> class Cage[+A <: Animal](val animal: A) defined class Cage Auf diese Weise können wir also die Type Erasure umgehen und dadurch an manchen Stellen ein einfacheres und besse- Wenn wir nun donaldsCage und operationFreedom neu erzeugen, res API schreiben. In Java hätten wir nämlich für die create-Me- dann sind wir am Ziel: thode einen Parameter vom Typ Class benötigt. Anwendungs- scala> operationFreedom(donaldsCage) beispiele für dieses Feature sind alle Tools, die für uns Instan- Donald is free now! zen aufgrund von Meta-Informationen erzeugen sollen, z. B. Dependency-Injection-Frameworks oder OR-Mapper. Doch wie geht Scala mit oben beschriebenem Problem um? Ganz einfach: Würden wir unseren Cage veränderlich machen, indem wir aus dem val ein var machen, dann bekommen wir ei- Case Classes und Pattern Matching nen Compiler-Fehler: Abschließend gehen wir noch auf ein besonders charakteristi- scala> class Cage[+A <: Animal](var animal: A) sches Scala-Feature ein, das eigentlich aus zwei Teilen besteht. <console>:6: error: covariant type A occurs in contravariant position in type A of parameter of setter animal_= Zunächst einmal können wir durch Voranstellen des Schlüs- class Cage[+A <: Animal](var animal: A) selwortes case eine Klasse zur sogenannten Case Class machen: scala> case class Cage[+A <: Animal](animal: A) Auf diese Weise stellt Scala schon durch das Typsystem und defined class Cage den Compiler sicher, dass wir in Sachen Varianz keine Fehler machen können. Es gäbe noch so einiges über Typparameter zu Dem aufmerksamen Leser wird auffallen, dass wir nun das erzählen, z. B. Kontravarianz und Untergrenzen, aber das wür- Schlüsselwort val vor dem Klassenparameter animal weglassen. de den Rahmen hier sprengen. Abschließend sei erwähnt, dass Das liegt daran, dass der Compiler für Case Classes die Klas- Typparameter in Scala zwar durchaus eine komplizierte Sache senparameter automatisch zu vals macht. Aber das ist nicht das sind. Aber das gilt nur für diejenigen von uns, die parametri- einzige Geschenk: Zusätzlich bekommen wir „sinnvolle“ Im- sierte Typen schreiben. Die Nutzung hingegen ist sehr einfach plementierungen von equals, hashCode und toString, allesamt auf und sicher. Basis der Klassenparameter implementiert. Darüber hinaus be- kommen wir auch noch automatisch ein Companion Object, welches die apply-Methode implementiert, um eine Case Class Die Type Erasure umgehen ohne das Schlüsselwort new zu erzeugen. Das Ganze sieht dann in der Anwendung so aus: Ergänzend zum Thema Typparameter wollen wir nun ein scala> val donaldsCage = Cage(new Duck("Donald")) wirklich innovatives Scala-Feature vorstellen. Wie in Java wer- donaldsCage: Cage[Duck] = Cage(Donald) den die Argumente, die im Quellcode für einen Typparameter verwendet werden, vom Compiler entfernt. Dieses Verhalten scala> donaldsCage.animal wird Type Erasure genannt und bewirkt, dass zur Laufzeit keine res6: Duck = Donald Informationen über die Typparameter vorliegen. Zum Beispiel können wir nicht ohne Weiteres ermitteln, mit welchem Typ ei- Wir sehen, dass wir unseren Cage nun ohne new erzeugen kön- ne einfache Methode parametrisiert ist: nen, die toString-Methode den Klassennamen und in Klam- scala> def create[T] = T.getClass.newInstance mern die toString-Aufrufe der Klassenparameter zurück gibt <console>:5: error: not found: value T und der Zugriff auf animal möglich ist, obwohl wir diesen Klas- def create[T] = T.getClass.newInstance senparameter nicht explizit zum val gemacht haben. Für sich alleine wären Case Classes zwar eine nette Erleich- Scala kennt zunächst das Konzept von impliziten Parametern. terung für mache Anwendungsfälle, aber so richtig zur Entfal- Wenn wir einer Parameterliste das Schlüsselwort implicit vo- tung kommen sie in Verbindung mit einem weiteren Sprach- ranstellen, dann müssen wir für diese Parameter nicht expli- Feature, dem Pattern Matching (musterbasierter Vergleich). zit Argumente übergeben, sofern der Compiler passende Werte Zunächst einmal könnte man Pattern Matching als „richtig findet, die er verwenden kann. Passend bedeutet in diesem Fall gemachte“ switch-Anweisung betrachten. Die offensichtlichen nicht nur, dass die Typen passen müssen, sondern die Werte Unterschiede sind, dass wir beliebige Ausdrücke übergeben müssen wiederum mit implicit definiert worden sein. Auf diese dürfen, dass es keinen Fall-Through gibt, d. h. nach einem er- Weise könnte eine Bibliothek zum Beispiel „vernünftige“ Vor- folgreichen Matching ist Schluss, und dass Pattern Matching einstellungen zur Verfügung stellen, die wir bei Bedarf expli- – ganz im Sinne der funktionalen Programmierung – immer zit überschreiben. ein Resultat ergibt. www.javaspektrum.de 39
  • 3. Fachthema  Die Syntax ist denkbar einfach: Wir schreiben einen Aus- äußeren Schalen beginnen. So können wir nach und nach im- druck, gefolgt vom Schlüsselwort match und anschließend einen mer mehr Scala einführen, ohne allzu große Risiken einzuge- Block mit case-Mustern: hen und ohne mit einem Schlag alles für alle Projektbeteiligten scala> def whatIsIt(x: Any) = x match { umstellen zu müssen. | case "Constant" => "It's a string constant." | case s: String => "It's the string " + s | case Cage(animal) => "It's a cage with a " + animal Fazit | case _ => "It's something else." | } whatIsIt: (x: Any)java.lang.String Es gäbe noch einiges mehr über Scala zu berichten, insbeson- dere über Standardbibliotheken wie z. B. die Actors-Biblio- scala> whatIsIt("Hello") thek. Aber was die Sprache selbst angeht, so haben wir im Rah- res7: java.lang.String = It's the string Hello men dieser dreiteiligen Serie die meisten Aspekte behandelt. scala> whatIsIt(donaldsCage) Das war möglich, weil Scala keine „breite“ Sprache ist, sondern res8: java.lang.String = It's a cage with a Donald eine „tiefe“: Die Sprache selbst ist recht schlank, wenngleich manche Features durchaus einen gewissen Tiefgang haben, z. Hier ist der Ausdruck der Parameter x der Methode whatIsIt. Al- B. das Typsystem. le case-Muster geben einen String zurück, sodass der Rückgabe- Dennoch ist es nicht erforderlich, gleich ganz abzutauchen typ der Methode ein String ist. Nun zu den einzelnen Mustern: und alles im Detail zu verstehen. Vielmehr ist es sogar dank  Wir prüfen zunächst mit einem Constant-Muster, ob unser der Schlankheit recht schnell möglich, Schwimmen zu lernen, Ausdruck eine Konstante darstellt. d. h. die Sprache so zu beherrschen, dass man schon etliche  Danach nutzen wir ein Variable-Muster mit Angabe des Vorteile im Vergleich zu Java nutzen kann. Man denke nur an Typs, um zu prüfen, ob ein String vorliegt. Dabei definieren die wesentlich kompaktere Notation und die Mächtigkeit von wir die Variable s, die wir auf der „rechten Seite“ des case- funktionalen Collections. Musters verwenden können. Wir hoffen, mit dieser Serie nicht nur Wissen über Scala ver-  Das dritte case-Muster schließlich nutzt unsere Case Class mittelt, sondern hoffentlich auch Lust auf Scala gemacht zu ha- Cage. Wir können hier einfach den Konstruktor der Case Class ben und freuen uns sehr auf Fragen oder Feedback. mit Variablen für seine Argumente hinschreiben und diese Variablen dann auf der „rechten Seite“ verwenden. Auf die- se Weise sparen wir uns eine explizite Prüfung auf den Typ Literatur und Links (instanceOf) sowie die Dekomposition in die Werte.  Abschließend verwenden wir das Wildcard-Muster, um si- [OdSpVe08] M. Odersky, L. Spoon, B. Venners, Programming in cherzustellen, dass wir alle möglichen Fälle abdecken, denn Scala: A comprehensive step-by-step guide, artima 2008 ansonsten könnte zur Laufzeit ein MatchError auftreten. [Scala] The Scala Programming Language, Der Vollständigkeit halber sei erwähnt, dass Pattern Matching http://www.scala-lang.org/ nicht nur mit Case Classes so elegant funktioniert. Das wäre [ScalaTest] Open-Source-Test-Framework, auch schade, denn wir wollen nicht alles zu Case Classes ma- http://www.scalatest.org/ chen und können das bei vorhandenen Klassen, z. B. aus einer [SeeBla10a] H. Seeberger, J. Blankenhorn, Scala: – Teil 1: Bibliothek, auch gar nicht. Es reicht vollkommen, dass wir ei- Einstieg, in: JavaSPEKTRUM, 2/2010 nen Extractor definieren: Ein Singleton Object mit einer unapply- [SeeBla10b] H. Seeberger, J. Blankenhorn, Scala: – Teil 2: Methode, welche die Dekomposition durchführt. FP und OOP, in: JavaSPEKTRUM, 3/2010 [Specs] A BDD Library for Scala, Project Hosting on Google Code, http://code.google.com/p/specs/ Scala und Java Zum Abschluss möchten wir noch kurz beleuchten, wie wir Scala in unsere Java-Projekte einbauen können. Das ist natür- lich möglich, denn Scala kompiliert zu Java-Bytecode und ist sogar hundertprozentig „abwärtskompatibel“ zu Java, sodass wir allen vorhandenen Java-Code verwenden können. Der um- Heiko Seeberger ist geschäftsführender Gesell- schafter der Weigle Wilczek GmbH und verantwortlich gekehrte Weg, d. h. Scala aus Java heraus zu nutzen, ist auch für die technologische Strategie des Unternehmens möglich, allerdings müssen wir uns dabei beim API auf dieje- mit den Schwerpunkten Java, Scala, OSGi, Eclipse RCP nigen Features beschränken, die Java kennt. und Lift. Zudem ist er aktiver Open Source Committer, Daher ist der folgende Weg vermutlich der einfachste: Wir Autor zahlreicher Fachartikel und Redner auf einschlä- beginnen damit, unsere Tests nicht mehr mit JUnit oder TestNG gigen Konferenzen. zu schreiben, sondern mit [Specs] oder [ScalaTest]. Das sind E-Mail: seeberger@weiglewilczek.com zwei Scala-Frameworks, die uns ermöglichen, sehr ausdrucks- Jan Blankenhorn ist Softwareentwickler bei der starke Testfälle zu schreiben, bei denen tatsächlich gilt, dass der Weigle Wilczek GmbH. Er entwickelt sowohl dyna- Code der Kommentar ist. Dabei sammeln wir wertvolle Praxis- mische Ajax-Webanwendungen als auch Rich Clients erfahrung mit Scala und können nach einer Weile dann dazu mit Eclipse RCP. Neben Softwareentwicklungs- und übergehen, einzelne Teilprojekte in Scala zu schreiben. -wartungsprojekten ist er als Trainer für Eclipse RCP im Dabei wäre es geschickt, wenn wir solche Teilprojekte identi- Rahmen der Eclipse Training Alliance aktiv. fizieren könnten, auf welche andere Teilprojekten keine Abhän- E-Mail: blankenhorn@weiglewilczek.com gigkeit haben. Oder mit anderen Worten: Wenn unser Projekt eine Zwiebel ist, bei der die äußeren Schalen von den inneren abhängen, aber nicht umgekehrt, dann sollten wir bei den ganz 40 JavaSPEKTRUM 4/2010