SlideShare uma empresa Scribd logo
1 de 3
Baixar para ler offline
Fachthema 

Das Beste aus zwei Welten                                                    Verwendung von override ist optional, weil wir ein abstraktes
                                                                             Member implementieren und nicht ein konkretes überschrei-

Scala – Teil 2:                                                              ben, dient jedoch dem Verständnis und der Sicherheit, Schreib-
                                                                             fehler zu vermeiden.

FP und OOP
                                                                               Nun wollen wir unseren Vogel fliegen lassen:
                                                                               scala> class Bird(name: String) extends DefaultAnimal(name) {
                                                                                 |   def fly = “I am flying!“
Heiko Seeberger, Jan Blankenhorn                                                 | }
                                                                               defined class Bird
Scala hat derzeit kräftigen Rückenwind, denn diese Sprache ist nicht
                                                                               scala> val bill = new Bird(“Bill“)
nur einfacher, sondern auch mächtiger als Java. Ein knapper und prä­
                                                                               bill: Bird = Bill
gnanter Programmierstil sowie neue Möglichkeiten durch funktionale
Programmierung lassen Entwicklerherzen höher schlagen. Dazu noch               scala> bill.fly
viel Flexibilität und volle Interoperabilität mit Java. Wie im ersten Teil     res0: java.lang.String = I am flying!
geht es auch hier um funktionale Programmierung, aber auch Objekt­
orientierung und Vererbung kommen nicht zu kurz.                             Und nun definieren wir noch einen Fish, der natürlich schwim-
                                                                             men kann:

Vererbung und Traits                                                           scala> class Fish(name: String) extends DefaultAnimal(name) {
                                                                                 |   def swim = “I am swimming!“
                                                                                 | }
    Wenn wir erst einmal nur Klassen betrachten, dann sieht
E   die Vererbung in Scala ganz ähnlich aus wie in Java. Las-
                                                                               defined class Fish


                                                                               scala> val freddy = new Fish(“Freddy“)
sen Sie uns zunächst die Klasse Animal definieren:
                                                                               freddy: Fish = Freddy
   scala> class Animal
   defined class Animal                                                        scala> freddy.swim
                                                                               res1: java.lang.String = I am swimming!
Und nun, davon abgeleitet, die Klasse                Bird,   indem wir das
Schlüsselwort extends verwenden:                                             So weit, so gut! Abgesehen von der knapperen Syntax gibt
   scala> class Bird extends Animal
                                                                             es bisher keinen elementaren Unterschied zu Java. Nun wol-
   defined class Bird                                                        len wir eine Ente definieren, die natürlich ein Vogel ist, aber
                                                                             auch schwimmen kann, und zwar genauso wie ein Fisch. Das
Analog zu Java können wir in Scala nur eine Superklasse er-                  ist zwar zugegebenermaßen etwas konstruiert, denn Fische
weitern. Selbstverständlich werden alle public und protected                 schwimmen bekanntlich anders als Enten. Aber „im echten Le-
Members (Felder und Methoden) vererbt.                                       ben“ möchten wir schon oft eine gemeinsame Basisimplemen-
  Auch für abstrakte Klassen, die mit dem Schlüsselwort ab-                  tierung verwenden, denn das reduziert Code und spart Zeit
stract definiert werden, gelten die aus Java bekannten Regeln.               und damit Kosten. Das ist in Java jedoch nicht möglich, denn
Allerdings können bzw. müssen wir bei abstrakten Members                     dort können wir nur von einer Klasse erben und Interfaces sind
auf das Schlüsselwort abstract verzichten und einfach die Zu-                ausschließlich abstrakt.
weisung des Feldes oder den Rumpf der Methode weglassen:                        Hier kommen in Scala Traits (auf Deutsch etwa Merkmale)
                                                                             ins Spiel, sozusagen Interfaces mit optionaler Implementie-
   scala> abstract class Animal {
                                                                             rung. Lassen Sie uns die Methode swim in den Trait Swimmer ausla-
     |   def name: String
     |   override def toString = name                                        gern, wobei wir das Schlüsselwort trait verwenden:
     | }
   defined class Animal                                                        scala> trait Swimmer {
                                                                                 |   def swim = “I am swimming!“
                                                                                 | }
Hiermit haben wir die abstrakte Klasse Animal mit der abstrak-                 defined trait Swimmer
ten Methode name definiert, die wir gleich verwenden, um to-
String zu überschreiben. Zur Erinnerung (s. auch [SeeBla10]):                Damit können wir zunächst unseren Fish neu definieren, indem
Die vertikalen Striche gehören nicht zum Scala-Code, sondern                 wir mit dem Schlüsselwort with den Trait Swimmer hinein mixen:
sind eine Besonderheit der REPL (Read Evaluate Print Loop)
                                                                               scala> class Fish(name: String) extends DefaultAnimal(name) with Swimmer
und signalisieren, dass die Eingabe als noch nicht abgeschlos-
                                                                               defined class Fish
sen betrachtet wird.
   Warum verwenden wir eine Methode für den Namen?
Nun ja, vielleicht können Tiere ja auch wie wir Menschen ih-                 Dann können wir uns der Klasse Duck zuwenden, die ein                        Bird
ren Namen ändern, z. B. bei einer Vogelhochzeit. In der Regel                mit hinein gemixtem Swimmer erweitert:
wird der Name aber konstant bleiben. Daher definieren wir
                                                                               scala> class Duck(name: String) extends Bird(name) with Swimmer
jetzt eine konkrete Basisklasse, welche die Methode name mit
                                                                               defined class Duck
einem gleichnamigen val (unveränderliches Feld) implemen-
tiert:                                                                         scala> val donald = new Duck(“Donald“)
                                                                               donald: Duck = Donald
   scala> class DefaultAnimal(override val name: String) extends Animal
   defined class DefaultAnimal
                                                                               scala> donald.fly
                                                                               res2: java.lang.String = I am flying!
Hiermit haben wir den Klassenparameter name durch das
Schlüsselwort val zum unveränderlichen Feld und dadurch                        scala> donald.swim
zum Parameter für den Primary Constructor gemacht. Die                         res3: java.lang.String = I am swimming!



 46                                                                                                                            JavaSPEKTRUM 3/2010
             Fachthema

Wie wir sehen, kann Donald sowohl fliegen als auch schwim-          Parameterliste aufgerufen. Der Unterschied tritt erst so rich-
men. Schön, aber ist das nicht Mehrfachvererbung? Ja und            tig zutage, wenn wir eine Funktion als Argument für den Auf-
nein. Mit Traits erzielen wir denselben Effekt wie bei klassi-      ruf einer anderen Methode/Funktion verwenden wollen: Ar-
scher Mehrfachvererbung und umgehen typische Probleme, z.           gumente müssen Objekte sein, sodass wir nur die Funktion
B. das Diamond-Problem [Wikipedia]. Ohne jetzt zu tief einzu-       verwenden können. Als Beispiel betrachten wir die Methode
steigen, heißt die Scala-Lösung Linearisierung: Beim Instanti-      Traversable.reduceLeft, die eine Funktion vom Typ Function2 nach
ieren eines Objekts werden alle vererbten und hinein gemix-         und nach auf alle Elemente anwendet:
ten Typen in eine lineare Reihenfolge gebracht, bei der das Ob-
                                                                      scala> val oneToFour = 1 to 4
jekt selbst am Anfang steht. Dadurch wird sichergestellt, dass
                                                                      oneToFour: ...Range.Inclusive... = Range(1, 2, 3, 4)
jederzeit klar ist, welcher Typ aufzurufen ist, was mit super ge-
meint ist und dass das betreffende Objekt das Verhalten der Su-       scala> oneToFour reduceLeft add
pertypen modifiziert und nicht umgekehrt. Für Details sei auf         res3: Int = 10

die einschlägige Literatur verwiesen [OdSpVe08].
  Neben dem statischen Hinein-Mixen von Traits, das wir bei         Hier ist Traversable der Supertyp aller Collections und 1 to 4
der Definition von neuen Typen einsetzen, können wir auch           nichts anderes als der Aufruf der Methode to auf dem Int-Wert
dynamische Mixins anwenden. Dabei ergänzen wir das An-              1, der aufgrund einer Implicit Conversion nach RichInt möglich
legen eines neuen Objekts einfach mit dem Schlüsselwort with        wird.
um den Trait, den wir dynamisch hinein mixen wollen:                   Nun ist die Verwendung von Funktionsliteralen nicht immer
                                                                    praktikabel. Insbesondere definieren wir Funktionalität in der
  scala> trait MichaelBuble extends Bird {
                                                                    OO-Welt häufig in Methoden und wollen diese gerne als Funk-
    |   override def fly = “I feel good and “ + super.fly
    | }                                                             tionen verwenden. Zum Glück können wir eine Methode leicht
  defined trait MichaelBuble                                        zu einer Funktion machen, indem wir sie durch Ersetzen der
                                                                    Parameterliste durch einen Unterstrich zu einer Partially Ap-
  scala> val michael = new Bird(“Michael“) with MichaelBuble
                                                                    plied Function machen. In unserem Beispiel müssen wir also
  michael: Bird with MichaelBuble = Michael
                                                                    die add-Funktion nicht „mühsam“ hinschreiben, sondern kön-
  scala> michael.fly                                                nen die add-Methode wieder verwenden:
  res4: java.lang.String = I feel good and I am flying!
                                                                      scala> val add = Calculator.add _
                                                                      add: (Int, Int) => Int = <function2>
Auf diese Weise erzielen wir einen ähnlichen Effekt wie bei der
aspektorientierten Programmierung, d. h. wir können Method-         Wir könnten natürlich auch ganz auf die Definition der add-
Interception ganz ohne dynamische Proxys oder Compiler-Er-          Funktion verzichten und direkt die Partially Applied Function
weiterungen (z. B. AspectJ) anwenden.                               an reduceLeft übergeben:
                                                                      scala> oneToFour reduceLeft Calculator.add _
                                                                      res4: Int = 10
Methoden und Funktionen
                                                                    Der Scala-Compiler bietet uns hier, wo eine Funktion erwar-
Wir sind bereits im ersten Artikel kurz auf funktionale Pro-        tet wird, eine abgekürzte Schreibweise, sodass wir den Unter-
grammierung in Scala eingegangen. Zur Erinnerung: Den be-           strich auch weglassen können. Dann sieht es so aus, als würden
sonderen Unterschied zu rein objektorientierten Sprachen wie        wir die add-Methode übergeben, aber in Wirklichkeit macht der
Java stellen Funktionen höherer Ordnung dar, d. h. Funktio-         Compiler daraus ein Funktionsobjekt:
nen, die andere Funktionen als Parameter erwarten.                    scala> oneToFour reduceLeft Calculator.add
   Was genau ist eigentlich der Unterschied zwischen einer Me-        res5: Int = 10
thode und einer Funktion? Von ihrer Wirkungsweise her sind
beide identisch, denn beide werden (mit Parametern) aufgeru-
fen und liefern ein Ergebnis zurück. Aber Methoden sind stets
Bestandteil einer Klasse, wohingegen Funktionen selbst Objek-       Curry und andere Gewürze
te sind. Zur Verdeutlichung ein kurzes Beispiel. Zunächst eine
Methode innerhalb eines Singleton Object:                           Was wir oben durch das Weglassen der gesamten Parameter-
  scala> object Calculator {
                                                                    liste gemacht haben, können wir auch auf einzelne Parameter
    |   def add(x: Int, y: Int) = x + y                             übertragen:
    | }
                                                                      scala> val addOne = Calculator.add(1, _: Int)
  defined module Calculator
                                                                      addOne: (Int) => Int = <function1>

  scala> Calculator.add(1, 2)
  res0: Int = 3                                                     Hier machen wir aus der add-Methode, die zwei Parameter hat,
                                                                    die addOne-Funktion mit einem Parameter, indem wir den ers-
Und nun eine analoge Funktion, definiert durch ein Funktions-       ten Parameter von add fixieren. Diese neue Funktion können
literal, d. h. eine „hingeschriebene Funktion“:                     wir dann dort verwenden, wo Funktionen mit einem Para-
                                                                    meter erwartet werden, z. B. als Argument der Methode Tra-
  scala> val add = (x: Int, y: Int) => x + y
                                                                    versable.map:
  add: (Int, Int) => Int = <function2>
                                                                      scala> oneToFour map addOne
  scala> add(1, 2)                                                    res6: ...IndexedSeq[Int] = IndexedSeq(2, 3, 4, 5)
  res1: Int = 3

                                                                    Hier bietet Scala auch noch eine interessante Alternative: Statt
Die hier gezeigte Verwendung erscheint quasi identisch, denn        einer Parameterliste mit zwei Parametern können wir auch
sowohl die Methode als auch die Funktion werden mit einer           zwei Parameterlisten mit je einem Parameter schreiben:

www.javaspektrum.de                                                                                                             47
Fachthema 

  scala> object Calculator {                                                     scala> oneToFour sortWith { (x, y) => x > y }
    |   def add(x: Int)(y: Int) = x + y                                          res10: ...IndexedSeq[Int] = IndexedSeq(4, 3, 2, 1)
    | }
  defined module Calculator
                                                                              Natürlich können wir Funktionen auch kombinieren, indem
Dieses Konzept, das natürlich mit beliebigen und beliebig vie-                wir die zweite auf dem Resultat der ersten aufrufen:
len Parameterlisten funktioniert, wird (nach dem Mathemati-                      scala> oneToFour filter { x => x %2 == 0 } map { x => x + 1 }
ker Haskell Brooks Curry) Currying genannt. Mit dem Unter-                       res11: ...IndexedSeq[Int] = IndexedSeq(3, 5)
strich lassen wir die letzte Parameterliste weg und können un-
sere addOne-Funktion nun so definieren:                                       Um alle Werte zu addieren, können wir, wie schon gezeigt, die
  scala> val addOne = Calculator.add(1) _                                     Methode reduceLeft verwenden. Eine ähnliche Methode ist fold-
  addOne: (Int) => Int = <function1>                                          Left, wobei wir hier einen Startwert angeben müssen, der einen
                                                                              anderen Typ haben darf, als derjenige der Collection:
Schön, aber wozu können wir das eigentlich verwenden? Zum                        scala> oneToFour.foldLeft(“#“) { (s, x) => s + x }
Beispiel, um mit „ganz normalen“ Sprachmitteln neue Kon-                         res12: java.lang.String = #1234
trollkonstrukte zu definieren, die sich wie Bestandteile der
Sprache anfühlen. Solche ausdrucksstarken Konstrukte bieten                   Wir könnten diese Beispiele schier endlos fortsetzen, denn die
sich an, um wiederkehrende Muster zu ersetzen, denn dadurch                   Scala-Collections bieten eine riesige Fülle an Methoden höhe-
wird die Codemenge verringert und die Verständlichkeit er-                    rer Ordnung. Dem interessierten Leser sei daher die Scala-Do-
höht. Ein Klassiker ist das Loan-Muster, bei dem eine Ressour-                kumentation [Scala] empfohlen, um sich weiter über Collec-
ce garantiert wieder geschlossen wird. Lassen Sie uns als kon-                tions zu orientieren.
kretes Beispiel das Schreiben in eine Datei betrachten:
  scala> import java.io._
  import java.io._                                                            Fazit
  scala> def withPrintWriter(file: String)(write: PrintWriter => Unit) {      Wir haben in diesem Artikel Vererbung und funktionale Pro-
    |   val out = new PrintWriter(file)
                                                                              grammierung genauer unter die Lupe genommen. Dabei sollte
    |   try {
    |     write(out)                                                          deutlich geworden sein, dass Traits, Funktionen höherer Ord-
    |   } finally {                                                           nung und Currying mächtige Features darstellen, um unseren
    |     out.close()                                                         Code vom Umfang her zu reduzieren und ausdrucksstärker zu
    |   }
                                                                              machen. Da wir viel Zeit damit verbringen, Code zu lesen, kön-
    | }
  withPrintWriter: (file: String)(write: (java.io.PrintWriter) => Unit)Unit
                                                                              nen wir mit Scala einen nennenswerten Produktivitätsgewinn
                                                                              erzielen. In der kommenden Folge werden wir uns dem Scala-
  scala> withPrintWriter(“test.txt“) { out =>                                 Typsystem widmen.
    | out.println(“TEST“)
    | }

Hier haben wir die Methode withPrintWriter mit zwei Parameter-                Literatur und Links
listen definiert. Immer wenn eine Parameterliste nur einen Pa-
rameter hat, können wir anstelle der runden auch geschweifte                  [OdSpVe08] M. Odersky, L. Spoon, B. Venners, Programming
Klammern verwenden. Dadurch erscheint die Anwendung die-                      in Scala: A comprehensive step-by-step guide, artima 2008,
ser Methode wie ein Kontrollkonstrukt, z. B. ähnlich wie while.               Kapitel 12.6
                                                                              [Scala] The Scala Programming Language,
                                                                              http://www.scala-lang.org/
Funktionale Collections                                                       [SeeBla10] H. Seeberger, J. Blankenhorn, Scala: – Teil 1: Einstieg,
                                                                              in: JavaSPEKTRUM, 2/2010
Es ist für funktionale Programmiersprachen typisch, mächtige                  [Wikipedia] Diamond-Problem,
Collection-Bibliotheken mitzubringen. Daher wollen wir die-                   http://de.wikipedia.org/wiki/Diamond-Problem
sen Artikel mit einigen Collection-Beispielen beschließen. Seit
Scala 2.8 erben alle Collection-Typen von Traversable, wo die
meisten Methoden definiert werden. Subtypen implementieren
diese gelegentlich optimiert und ergänzen spezifische Methoden.                                    Heiko Seeberger ist geschäftsführender Gesell-
                                                                                                   schafter der Weigle Wilczek GmbH und verantwortlich
  Über Collections können wir mit foreach iterieren, um einen
                                                                                                   für die technologische Strategie des Unternehmens
Seiteneffekt zu erzielen. Das ist zwar nicht die reine FP-Lehre,
                                                                                                   mit den Schwerpunkten Java, Scala, OSGi, Eclipse RCP
aber pragmatisches Scala:                                                                          und Lift. Zudem ist er aktiver Open Source Committer,
  scala> oneToFour foreach print                                                                   Autor zahlreicher Fachartikel und Redner auf einschlä-
  1234                                                                                             gigen Konferenzen.
                                                                                                   E-Mail: seeberger@weiglewilczek.com.
Die Methode filter liefert eine neue Collection zurück, die nur
                                                                                                   Jan Blankenhorn ist Softwareentwickler bei der
die Elemente enthält, für welche die übergebene Funktion true                                      Weigle Wilczek GmbH. Er entwickelt sowohl dyna-
ergibt:                                                                                            mische Ajax-Webanwendungen als auch Rich Clients
  scala> oneToFour filter { x => x %2 == 0 }                                                       mit Eclipse RCP. Neben Softwareentwicklungs- und
  res9: ...IndexedSeq[Int] = IndexedSeq(2, 4)                                                      -wartungsprojekten ist er als Trainer für Eclipse RCP im
                                                                                                   Rahmen der Eclipse Training Alliance aktiv.
Die Methode sort erwartet eine Funktion, die zwei Elemente ver-                                    E-Mail: blankenhorn@weiglewilczek.com.
gleicht und true zurück liefert, wenn das erste vor dem zweiten
kommen soll. Also drehen wir unsere Sequenz doch einfach um:

 48                                                                                                                              JavaSPEKTRUM 3/2010

Mais conteúdo relacionado

Destaque

Julianbeever dibujosenlaacera
Julianbeever dibujosenlaaceraJulianbeever dibujosenlaacera
Julianbeever dibujosenlaacera
Alvaro Caraballo
 
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
Heiko Seeberger
 
Actividad parque chicaque sara
Actividad parque chicaque saraActividad parque chicaque sara
Actividad parque chicaque sara
tauritho
 
Presentación ARSChile (2010)
Presentación ARSChile (2010)Presentación ARSChile (2010)
Presentación ARSChile (2010)
ARSChile
 
Convenio lo prado ONG CIPDEL
Convenio lo prado ONG CIPDELConvenio lo prado ONG CIPDEL
Convenio lo prado ONG CIPDEL
CIPDEL
 
Die Vergangenheit
Die VergangenheitDie Vergangenheit
Die Vergangenheit
s ghani
 
Evaluación de inteligencias múltiples
Evaluación de inteligencias múltiplesEvaluación de inteligencias múltiples
Evaluación de inteligencias múltiples
Robertoruedaalvarado
 

Destaque (20)

Opening Session: Jugend-Jugendarbeit-Zukunft
Opening Session: Jugend-Jugendarbeit-ZukunftOpening Session: Jugend-Jugendarbeit-Zukunft
Opening Session: Jugend-Jugendarbeit-Zukunft
 
JM 04/09 - OSGi in kleinen Dosen 5
JM 04/09 - OSGi in kleinen Dosen 5JM 04/09 - OSGi in kleinen Dosen 5
JM 04/09 - OSGi in kleinen Dosen 5
 
Julianbeever dibujosenlaacera
Julianbeever dibujosenlaaceraJulianbeever dibujosenlaacera
Julianbeever dibujosenlaacera
 
Contextualización
ContextualizaciónContextualización
Contextualización
 
Contextualización
ContextualizaciónContextualización
Contextualización
 
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
 
Actividad parque chicaque sara
Actividad parque chicaque saraActividad parque chicaque sara
Actividad parque chicaque sara
 
Diapositivas leidy
Diapositivas leidyDiapositivas leidy
Diapositivas leidy
 
Actividad parque chicaque vivis
Actividad parque chicaque vivisActividad parque chicaque vivis
Actividad parque chicaque vivis
 
Práctica 23
Práctica 23Práctica 23
Práctica 23
 
Feria del libro
Feria del libroFeria del libro
Feria del libro
 
MENSAJE
MENSAJEMENSAJE
MENSAJE
 
Presentación ARSChile (2010)
Presentación ARSChile (2010)Presentación ARSChile (2010)
Presentación ARSChile (2010)
 
Convenio lo prado ONG CIPDEL
Convenio lo prado ONG CIPDELConvenio lo prado ONG CIPDEL
Convenio lo prado ONG CIPDEL
 
Maschinenbaustudium 2008: Mit der BELL zu SIEMENS und dann in die Welt hinaus
Maschinenbaustudium 2008: Mit der BELL zu SIEMENS und dann in die Welt hinausMaschinenbaustudium 2008: Mit der BELL zu SIEMENS und dann in die Welt hinaus
Maschinenbaustudium 2008: Mit der BELL zu SIEMENS und dann in die Welt hinaus
 
Die Vergangenheit
Die VergangenheitDie Vergangenheit
Die Vergangenheit
 
Practica 4
Practica 4Practica 4
Practica 4
 
El amor.ppt finaaaal
El amor.ppt finaaaalEl amor.ppt finaaaal
El amor.ppt finaaaal
 
Jesse riaño chicaque
Jesse riaño chicaqueJesse riaño chicaque
Jesse riaño chicaque
 
Evaluación de inteligencias múltiples
Evaluación de inteligencias múltiplesEvaluación de inteligencias múltiples
Evaluación de inteligencias múltiples
 

Mais de Heiko Seeberger

Eclipse Magazin 12 - Design by Contract
Eclipse Magazin 12 - Design by ContractEclipse Magazin 12 - Design by Contract
Eclipse Magazin 12 - Design by Contract
Heiko Seeberger
 
Eclipse Magazin15 - Performance Logging
Eclipse Magazin15 - Performance LoggingEclipse Magazin15 - Performance Logging
Eclipse Magazin15 - Performance Logging
Heiko 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 Equinox
Heiko Seeberger
 
Eclipse Magazin 12 - Security does matter
Eclipse Magazin 12 - Security does matterEclipse Magazin 12 - Security does matter
Eclipse Magazin 12 - Security does matter
Heiko Seeberger
 
EclipseCon 08 - Agile RCP
EclipseCon 08 - Agile RCPEclipseCon 08 - Agile RCP
EclipseCon 08 - Agile RCP
Heiko Seeberger
 
W-JAX 07 - AOP im Einsatz mit OSGi und RCP
W-JAX 07 - AOP im Einsatz mit OSGi und RCPW-JAX 07 - AOP im Einsatz mit OSGi und RCP
W-JAX 07 - AOP im Einsatz mit OSGi und RCP
Heiko Seeberger
 

Mais de 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
 
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 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
 
W-JAX 07 - AOP im Einsatz mit OSGi und RCP
W-JAX 07 - AOP im Einsatz mit OSGi und RCPW-JAX 07 - AOP im Einsatz mit OSGi und RCP
W-JAX 07 - AOP im Einsatz mit OSGi und RCP
 
W-JAX 08 - Declarative Services versus Spring Dynamic Modules
W-JAX 08 - Declarative Services versus Spring Dynamic ModulesW-JAX 08 - Declarative Services versus Spring Dynamic Modules
W-JAX 08 - Declarative Services versus Spring Dynamic Modules
 

JavaSPEKTRUM - Scala 2

  • 1. Fachthema  Das Beste aus zwei Welten Verwendung von override ist optional, weil wir ein abstraktes Member implementieren und nicht ein konkretes überschrei- Scala – Teil 2: ben, dient jedoch dem Verständnis und der Sicherheit, Schreib- fehler zu vermeiden. FP und OOP Nun wollen wir unseren Vogel fliegen lassen: scala> class Bird(name: String) extends DefaultAnimal(name) { | def fly = “I am flying!“ Heiko Seeberger, Jan Blankenhorn | } defined class Bird Scala hat derzeit kräftigen Rückenwind, denn diese Sprache ist nicht scala> val bill = new Bird(“Bill“) nur einfacher, sondern auch mächtiger als Java. Ein knapper und prä­ bill: Bird = Bill gnanter Programmierstil sowie neue Möglichkeiten durch funktionale Programmierung lassen Entwicklerherzen höher schlagen. Dazu noch scala> bill.fly viel Flexibilität und volle Interoperabilität mit Java. Wie im ersten Teil res0: java.lang.String = I am flying! geht es auch hier um funktionale Programmierung, aber auch Objekt­ orientierung und Vererbung kommen nicht zu kurz. Und nun definieren wir noch einen Fish, der natürlich schwim- men kann: Vererbung und Traits scala> class Fish(name: String) extends DefaultAnimal(name) { | def swim = “I am swimming!“ | } Wenn wir erst einmal nur Klassen betrachten, dann sieht E die Vererbung in Scala ganz ähnlich aus wie in Java. Las- defined class Fish scala> val freddy = new Fish(“Freddy“) sen Sie uns zunächst die Klasse Animal definieren: freddy: Fish = Freddy scala> class Animal defined class Animal scala> freddy.swim res1: java.lang.String = I am swimming! Und nun, davon abgeleitet, die Klasse Bird, indem wir das Schlüsselwort extends verwenden: So weit, so gut! Abgesehen von der knapperen Syntax gibt scala> class Bird extends Animal es bisher keinen elementaren Unterschied zu Java. Nun wol- defined class Bird len wir eine Ente definieren, die natürlich ein Vogel ist, aber auch schwimmen kann, und zwar genauso wie ein Fisch. Das Analog zu Java können wir in Scala nur eine Superklasse er- ist zwar zugegebenermaßen etwas konstruiert, denn Fische weitern. Selbstverständlich werden alle public und protected schwimmen bekanntlich anders als Enten. Aber „im echten Le- Members (Felder und Methoden) vererbt. ben“ möchten wir schon oft eine gemeinsame Basisimplemen- Auch für abstrakte Klassen, die mit dem Schlüsselwort ab- tierung verwenden, denn das reduziert Code und spart Zeit stract definiert werden, gelten die aus Java bekannten Regeln. und damit Kosten. Das ist in Java jedoch nicht möglich, denn Allerdings können bzw. müssen wir bei abstrakten Members dort können wir nur von einer Klasse erben und Interfaces sind auf das Schlüsselwort abstract verzichten und einfach die Zu- ausschließlich abstrakt. weisung des Feldes oder den Rumpf der Methode weglassen: Hier kommen in Scala Traits (auf Deutsch etwa Merkmale) ins Spiel, sozusagen Interfaces mit optionaler Implementie- scala> abstract class Animal { rung. Lassen Sie uns die Methode swim in den Trait Swimmer ausla- | def name: String | override def toString = name gern, wobei wir das Schlüsselwort trait verwenden: | } defined class Animal scala> trait Swimmer { | def swim = “I am swimming!“ | } Hiermit haben wir die abstrakte Klasse Animal mit der abstrak- defined trait Swimmer ten Methode name definiert, die wir gleich verwenden, um to- String zu überschreiben. Zur Erinnerung (s. auch [SeeBla10]): Damit können wir zunächst unseren Fish neu definieren, indem Die vertikalen Striche gehören nicht zum Scala-Code, sondern wir mit dem Schlüsselwort with den Trait Swimmer hinein mixen: sind eine Besonderheit der REPL (Read Evaluate Print Loop) scala> class Fish(name: String) extends DefaultAnimal(name) with Swimmer und signalisieren, dass die Eingabe als noch nicht abgeschlos- defined class Fish sen betrachtet wird. Warum verwenden wir eine Methode für den Namen? Nun ja, vielleicht können Tiere ja auch wie wir Menschen ih- Dann können wir uns der Klasse Duck zuwenden, die ein Bird ren Namen ändern, z. B. bei einer Vogelhochzeit. In der Regel mit hinein gemixtem Swimmer erweitert: wird der Name aber konstant bleiben. Daher definieren wir scala> class Duck(name: String) extends Bird(name) with Swimmer jetzt eine konkrete Basisklasse, welche die Methode name mit defined class Duck einem gleichnamigen val (unveränderliches Feld) implemen- tiert: scala> val donald = new Duck(“Donald“) donald: Duck = Donald scala> class DefaultAnimal(override val name: String) extends Animal defined class DefaultAnimal scala> donald.fly res2: java.lang.String = I am flying! Hiermit haben wir den Klassenparameter name durch das Schlüsselwort val zum unveränderlichen Feld und dadurch scala> donald.swim zum Parameter für den Primary Constructor gemacht. Die res3: java.lang.String = I am swimming! 46 JavaSPEKTRUM 3/2010
  • 2. Fachthema Wie wir sehen, kann Donald sowohl fliegen als auch schwim- Parameterliste aufgerufen. Der Unterschied tritt erst so rich- men. Schön, aber ist das nicht Mehrfachvererbung? Ja und tig zutage, wenn wir eine Funktion als Argument für den Auf- nein. Mit Traits erzielen wir denselben Effekt wie bei klassi- ruf einer anderen Methode/Funktion verwenden wollen: Ar- scher Mehrfachvererbung und umgehen typische Probleme, z. gumente müssen Objekte sein, sodass wir nur die Funktion B. das Diamond-Problem [Wikipedia]. Ohne jetzt zu tief einzu- verwenden können. Als Beispiel betrachten wir die Methode steigen, heißt die Scala-Lösung Linearisierung: Beim Instanti- Traversable.reduceLeft, die eine Funktion vom Typ Function2 nach ieren eines Objekts werden alle vererbten und hinein gemix- und nach auf alle Elemente anwendet: ten Typen in eine lineare Reihenfolge gebracht, bei der das Ob- scala> val oneToFour = 1 to 4 jekt selbst am Anfang steht. Dadurch wird sichergestellt, dass oneToFour: ...Range.Inclusive... = Range(1, 2, 3, 4) jederzeit klar ist, welcher Typ aufzurufen ist, was mit super ge- meint ist und dass das betreffende Objekt das Verhalten der Su- scala> oneToFour reduceLeft add pertypen modifiziert und nicht umgekehrt. Für Details sei auf res3: Int = 10 die einschlägige Literatur verwiesen [OdSpVe08]. Neben dem statischen Hinein-Mixen von Traits, das wir bei Hier ist Traversable der Supertyp aller Collections und 1 to 4 der Definition von neuen Typen einsetzen, können wir auch nichts anderes als der Aufruf der Methode to auf dem Int-Wert dynamische Mixins anwenden. Dabei ergänzen wir das An- 1, der aufgrund einer Implicit Conversion nach RichInt möglich legen eines neuen Objekts einfach mit dem Schlüsselwort with wird. um den Trait, den wir dynamisch hinein mixen wollen: Nun ist die Verwendung von Funktionsliteralen nicht immer praktikabel. Insbesondere definieren wir Funktionalität in der scala> trait MichaelBuble extends Bird { OO-Welt häufig in Methoden und wollen diese gerne als Funk- | override def fly = “I feel good and “ + super.fly | } tionen verwenden. Zum Glück können wir eine Methode leicht defined trait MichaelBuble zu einer Funktion machen, indem wir sie durch Ersetzen der Parameterliste durch einen Unterstrich zu einer Partially Ap- scala> val michael = new Bird(“Michael“) with MichaelBuble plied Function machen. In unserem Beispiel müssen wir also michael: Bird with MichaelBuble = Michael die add-Funktion nicht „mühsam“ hinschreiben, sondern kön- scala> michael.fly nen die add-Methode wieder verwenden: res4: java.lang.String = I feel good and I am flying! scala> val add = Calculator.add _ add: (Int, Int) => Int = <function2> Auf diese Weise erzielen wir einen ähnlichen Effekt wie bei der aspektorientierten Programmierung, d. h. wir können Method- Wir könnten natürlich auch ganz auf die Definition der add- Interception ganz ohne dynamische Proxys oder Compiler-Er- Funktion verzichten und direkt die Partially Applied Function weiterungen (z. B. AspectJ) anwenden. an reduceLeft übergeben: scala> oneToFour reduceLeft Calculator.add _ res4: Int = 10 Methoden und Funktionen Der Scala-Compiler bietet uns hier, wo eine Funktion erwar- Wir sind bereits im ersten Artikel kurz auf funktionale Pro- tet wird, eine abgekürzte Schreibweise, sodass wir den Unter- grammierung in Scala eingegangen. Zur Erinnerung: Den be- strich auch weglassen können. Dann sieht es so aus, als würden sonderen Unterschied zu rein objektorientierten Sprachen wie wir die add-Methode übergeben, aber in Wirklichkeit macht der Java stellen Funktionen höherer Ordnung dar, d. h. Funktio- Compiler daraus ein Funktionsobjekt: nen, die andere Funktionen als Parameter erwarten. scala> oneToFour reduceLeft Calculator.add Was genau ist eigentlich der Unterschied zwischen einer Me- res5: Int = 10 thode und einer Funktion? Von ihrer Wirkungsweise her sind beide identisch, denn beide werden (mit Parametern) aufgeru- fen und liefern ein Ergebnis zurück. Aber Methoden sind stets Bestandteil einer Klasse, wohingegen Funktionen selbst Objek- Curry und andere Gewürze te sind. Zur Verdeutlichung ein kurzes Beispiel. Zunächst eine Methode innerhalb eines Singleton Object: Was wir oben durch das Weglassen der gesamten Parameter- scala> object Calculator { liste gemacht haben, können wir auch auf einzelne Parameter | def add(x: Int, y: Int) = x + y übertragen: | } scala> val addOne = Calculator.add(1, _: Int) defined module Calculator addOne: (Int) => Int = <function1> scala> Calculator.add(1, 2) res0: Int = 3 Hier machen wir aus der add-Methode, die zwei Parameter hat, die addOne-Funktion mit einem Parameter, indem wir den ers- Und nun eine analoge Funktion, definiert durch ein Funktions- ten Parameter von add fixieren. Diese neue Funktion können literal, d. h. eine „hingeschriebene Funktion“: wir dann dort verwenden, wo Funktionen mit einem Para- meter erwartet werden, z. B. als Argument der Methode Tra- scala> val add = (x: Int, y: Int) => x + y versable.map: add: (Int, Int) => Int = <function2> scala> oneToFour map addOne scala> add(1, 2) res6: ...IndexedSeq[Int] = IndexedSeq(2, 3, 4, 5) res1: Int = 3 Hier bietet Scala auch noch eine interessante Alternative: Statt Die hier gezeigte Verwendung erscheint quasi identisch, denn einer Parameterliste mit zwei Parametern können wir auch sowohl die Methode als auch die Funktion werden mit einer zwei Parameterlisten mit je einem Parameter schreiben: www.javaspektrum.de 47
  • 3. Fachthema  scala> object Calculator { scala> oneToFour sortWith { (x, y) => x > y } | def add(x: Int)(y: Int) = x + y res10: ...IndexedSeq[Int] = IndexedSeq(4, 3, 2, 1) | } defined module Calculator Natürlich können wir Funktionen auch kombinieren, indem Dieses Konzept, das natürlich mit beliebigen und beliebig vie- wir die zweite auf dem Resultat der ersten aufrufen: len Parameterlisten funktioniert, wird (nach dem Mathemati- scala> oneToFour filter { x => x %2 == 0 } map { x => x + 1 } ker Haskell Brooks Curry) Currying genannt. Mit dem Unter- res11: ...IndexedSeq[Int] = IndexedSeq(3, 5) strich lassen wir die letzte Parameterliste weg und können un- sere addOne-Funktion nun so definieren: Um alle Werte zu addieren, können wir, wie schon gezeigt, die scala> val addOne = Calculator.add(1) _ Methode reduceLeft verwenden. Eine ähnliche Methode ist fold- addOne: (Int) => Int = <function1> Left, wobei wir hier einen Startwert angeben müssen, der einen anderen Typ haben darf, als derjenige der Collection: Schön, aber wozu können wir das eigentlich verwenden? Zum scala> oneToFour.foldLeft(“#“) { (s, x) => s + x } Beispiel, um mit „ganz normalen“ Sprachmitteln neue Kon- res12: java.lang.String = #1234 trollkonstrukte zu definieren, die sich wie Bestandteile der Sprache anfühlen. Solche ausdrucksstarken Konstrukte bieten Wir könnten diese Beispiele schier endlos fortsetzen, denn die sich an, um wiederkehrende Muster zu ersetzen, denn dadurch Scala-Collections bieten eine riesige Fülle an Methoden höhe- wird die Codemenge verringert und die Verständlichkeit er- rer Ordnung. Dem interessierten Leser sei daher die Scala-Do- höht. Ein Klassiker ist das Loan-Muster, bei dem eine Ressour- kumentation [Scala] empfohlen, um sich weiter über Collec- ce garantiert wieder geschlossen wird. Lassen Sie uns als kon- tions zu orientieren. kretes Beispiel das Schreiben in eine Datei betrachten: scala> import java.io._ import java.io._ Fazit scala> def withPrintWriter(file: String)(write: PrintWriter => Unit) { Wir haben in diesem Artikel Vererbung und funktionale Pro- | val out = new PrintWriter(file) grammierung genauer unter die Lupe genommen. Dabei sollte | try { | write(out) deutlich geworden sein, dass Traits, Funktionen höherer Ord- | } finally { nung und Currying mächtige Features darstellen, um unseren | out.close() Code vom Umfang her zu reduzieren und ausdrucksstärker zu | } machen. Da wir viel Zeit damit verbringen, Code zu lesen, kön- | } withPrintWriter: (file: String)(write: (java.io.PrintWriter) => Unit)Unit nen wir mit Scala einen nennenswerten Produktivitätsgewinn erzielen. In der kommenden Folge werden wir uns dem Scala- scala> withPrintWriter(“test.txt“) { out => Typsystem widmen. | out.println(“TEST“) | } Hier haben wir die Methode withPrintWriter mit zwei Parameter- Literatur und Links listen definiert. Immer wenn eine Parameterliste nur einen Pa- rameter hat, können wir anstelle der runden auch geschweifte [OdSpVe08] M. Odersky, L. Spoon, B. Venners, Programming Klammern verwenden. Dadurch erscheint die Anwendung die- in Scala: A comprehensive step-by-step guide, artima 2008, ser Methode wie ein Kontrollkonstrukt, z. B. ähnlich wie while. Kapitel 12.6 [Scala] The Scala Programming Language, http://www.scala-lang.org/ Funktionale Collections [SeeBla10] H. Seeberger, J. Blankenhorn, Scala: – Teil 1: Einstieg, in: JavaSPEKTRUM, 2/2010 Es ist für funktionale Programmiersprachen typisch, mächtige [Wikipedia] Diamond-Problem, Collection-Bibliotheken mitzubringen. Daher wollen wir die- http://de.wikipedia.org/wiki/Diamond-Problem sen Artikel mit einigen Collection-Beispielen beschließen. Seit Scala 2.8 erben alle Collection-Typen von Traversable, wo die meisten Methoden definiert werden. Subtypen implementieren diese gelegentlich optimiert und ergänzen spezifische Methoden. Heiko Seeberger ist geschäftsführender Gesell- schafter der Weigle Wilczek GmbH und verantwortlich Über Collections können wir mit foreach iterieren, um einen für die technologische Strategie des Unternehmens Seiteneffekt zu erzielen. Das ist zwar nicht die reine FP-Lehre, mit den Schwerpunkten Java, Scala, OSGi, Eclipse RCP aber pragmatisches Scala: und Lift. Zudem ist er aktiver Open Source Committer, scala> oneToFour foreach print Autor zahlreicher Fachartikel und Redner auf einschlä- 1234 gigen Konferenzen. E-Mail: seeberger@weiglewilczek.com. Die Methode filter liefert eine neue Collection zurück, die nur Jan Blankenhorn ist Softwareentwickler bei der die Elemente enthält, für welche die übergebene Funktion true Weigle Wilczek GmbH. Er entwickelt sowohl dyna- ergibt: mische Ajax-Webanwendungen als auch Rich Clients scala> oneToFour filter { x => x %2 == 0 } mit Eclipse RCP. Neben Softwareentwicklungs- und res9: ...IndexedSeq[Int] = IndexedSeq(2, 4) -wartungsprojekten ist er als Trainer für Eclipse RCP im Rahmen der Eclipse Training Alliance aktiv. Die Methode sort erwartet eine Funktion, die zwei Elemente ver- E-Mail: blankenhorn@weiglewilczek.com. gleicht und true zurück liefert, wenn das erste vor dem zweiten kommen soll. Also drehen wir unsere Sequenz doch einfach um: 48 JavaSPEKTRUM 3/2010