Scala. Abstrakte Elemente Jevgeni Zelenkov. HM. 2010.
Was sind abstrakte Elemente Abstrakte Elemente sind Elemente in Traits oder Klassen, die keine konkrete Implementierung haben. Abstrakte Klassen und Traits sind keine abstrakten Elemente! trait UnitConverter { type T def convert(x: T): T val original: T var converted: T class FeetToMetersConverter extends UnitConverter { type T = Double def convert(x: T) = x * 0.3048 val original = 10.0 var converted = convert (original)
Abstrakter Typ 1) deklariert einen abstrakten Typ, der später implementiert werden muss. 2) als Alias (macht den Code sauberer und lesbarer). trait Car { type Engine class DieselEngine class DieselCar extends Car { type Engine = DieselEngine class VeryImportantClassWhichMakesMagic class Navi { type gps = VeryImportantClassWhichMakesMagic def getcoordinates(gps)...
Type bounds 1/2 Definieren Sub-/ Superklassen für den Typen: type myclass <: Super type myclass >: Sub type myclass >: Sub <: Super trait Fuel trait Diesel extends Fuel trait BioDiesel extends Diesel trait BioDieselPlus extends BioDiesel class BioDieselPlusCar{ type suitablefuel <: BioDieselPlus class VWPassat extends BioDieselPlusCar { type suitablefuel = BioDieselPlus
Type bounds 2/2 Definieren Sub-/ Superklassen für den Typen: type myclass <: Super type myclass >: Sub type myclass >: Sub <: Super trait Fuel trait Diesel extends Fuel trait BioDiesel extends Diesel trait BioDieselPlus extends BioDiesel class NormalDieselCar{ type suitablefuel >: BioDieselPlus <: Diesel class VWGolf extends NormalDieselCar { type suitablefuel = Diesel // BioDiesel // BioDieselPlus
Abstrakte Methode Abstrakte Methode setzt nur Name und Typ ein, keinen Wert. Konkrete Implementierung setzt Wert ein. Abstrakte Methoden kann man entweder als konkrete def, val oder var implementieren. trait Greeting { def msg: String class ShipGreeting extends Greeting { def msg = Welcome aboard! //val msg = Welcome aboard! // OK //var msg = Welcome aboard! // OK
Abstrakte var 1/2 Abstrakte var setzt nur Name und Typ ein, keinen Wert. Konkrete Implementierung setzt Wert ein. Konkrete Implementierung muss ein var sein. def oder val werden nicht kompiliert. Getter and Setter werden eingefügt. trait Greeting { var msg: String class ShipGreeting extends Greeting { var msg = Welcome aboard! //val msg = Welcome aboard! // Fehler //def msg = Welcome aboard! // Fehler
Abstrakte var 2/2 Konkrete vars kommen mit Getter und Setter. Das gleiche gilt auch für abstrakte vars. Die 2 Traits unten sind identisch. Datenfeld für 'msg' in konkreter Klasse implementiert. trait Greeting { var msg: String trait Greeting { def msg : String def msg_=(x: String) // Getter // Setter
Abstrakte val Abstrakte val setzt nur Name und Typ ein, keinen Wert. Konkrete Implementierung setzt Wert ein. Konkrete Implementierung muss val sein. def oder var werden nicht kompiliert. trait Greeting { val msg: String class ShipGreeting extends Greeting { val msg = Welcome aboard! // var msg = Welcome aboard! // Fehler // def msg = Welcome aboard! // Fehler
Initialisierung von vals 1/2 vals werden manchmal wie Parameter für Traits benutzt, da Traits keinen Konstruktoren haben dürfen. 1. Trait wird initialisiert 2. Werte werden berechnet 3. anonyme Klasse wird mit den Werten initialisiert
Initialisierung von vals 2/2 Beispiel: trait Number{ val number: Double val inverse = inversify def inversify = 1.0 / number val x = 2 val t = new Number { val number = 10.0 * x println( t.inverse ) => Infinity
Lösungen Mögliche Lösungen: 1. pre-initialisierte Objektvariablen 2. lazy vals
pre-initialisierte Objektvariablen 1 Möglichkeit, Objektvariablen noch vor dem Klassenaufruf zu initialisieren. trait Number{ val number: Double val inverse = inversify def inversify = 1.0 / number val x = 2 new { val number = 10.0 * x with Number
pre-initialisierte Objektvariablen 2 pre-initialisierte Objektvariablen kann man nicht nur mit anonymen Klassen benutzen, sondern auch mit Objekten, normalen Klassen usw. trait Number{ val number: Double val inverse = inversify def inversify = 1.0 / number val i = 2 class NumberClass(n: Double) extends { val number = n * i with Number
pre-initialisierte Objektvariablen 3 "this" darf man in pre-initialisierten Objektvariablen nicht benutzen. trait Number{ val number: Double val inverse: Double val i = 2 class NumberClass extends { val number = 20 * i val inverse = 1/this.number // Fehler with Number
lazy vals Der Wert von lazy val wird erst dann berechnet, wenn er tatsächlich benutzt wird. trait Number{ val number: Double lazy val inverse = inversify def inversify = 1.0 / number val x = 2 new Number { val number = 10.0 * x
Scala type System 1/2 Beispiel: class Fuel abstract class Car{ def tankup (fuel: Fuel) class Diesel extends Fuel class DieselCar extends Car{ override def tankup (fuel: Diesel) { // Fehler class Gasoline extends Fuel val vwgolf: DieselCar = new Car vwgolf.tankup(new Gasoline) // Wäre möglich
Scala type System 2/2 Scala Lösung: class Fuel abstract class Car{ type SuitableFuel <: Fuel def tankup(fuel: SuitableFuel) class Diesel extends Fuel // OK class DieselCar extends Car{ type SuitableFuel = Diesel override def tankup (fuel: SuitableFuel) { class Gasoline extends Fuel val vwgolf: DieselCar = new DieselCar vwgolf.tankup(new Gasoline) // vwgolf.suitablefuel!= Gasoline
path-dependent Typen 1/2 Scala unterscheidet zwischen Inneren Klassen und pathdependent Typen. class Outer { class Inner val o1 = new Outer val o2 = new Inner Implementierung: (new Outer).Inner Typ: Outer#Inner new o1.inner new Outer#Inner // OK // Fehler
path-dependent Typen 2/2 Ähnlich wie die Inneren Klassen in Java: Inneren Klassen Path-dependent Typ Syntax Outer.Inner Outer#Inner Gebunden an einer Konkreter Instanz von Ausseren Klasse generell an eine Ausserklasse, nicht auf Konkrete Instanz davon Insanzierbar? Ja (Ausser - Klasse) Nein (Ausser - Objekt)
path-dependent Typen Beispiel: LIVE BEISPIELE
Enumeratoren 1/3 Enumeratoren in Scala = Objekte, die von "Enumeration"-Klasse abgeleitet sind. object Color extends Enumeration { val Red = Value val Green = Value val Blue = Value ///// ODER ////// object Color extends Enumeration { val Red, Green, Blue = Value
Enumeratoren 2/3 Enumeratoren kann man importieren. Object Color extends Enumeration{ val Red = Value import Color._ Red => res0: Color.Value = Color(0)
Enumeratoren 3/3 Scala Enums bietet zusätzliche Funktionalität, z.b.: Werte mit Namen überladen for-schleifen über Enum-Werte enum-wert mit Index ansprechen object Direction extends Enumeration { val North = Value("N") val East = Value("E") val South = Value("S") val West = Value("W") Direction(2) => Direction.Value = South("S")
Zusammenfassung Scala bietet eine gute Unterstützung für OO-Abstraktion. Das erlaubt die Entwicklung einer Anwendung möglichst lang abstrakt zu halten und die konkrete Implementierung ganz am Ende zu schreiben.
Scala. Abstrakte Elemente Danke für die Aufmerksamkeit! Fragen!?