Funktionen / Methoden
EinstiegMethoden / Funktionsaufruf
Setter und Getter
Variable Parameterzahl / wiederholte Parameter
Benannte Argumente (engl. named arguments)
Standardargumente (engl. default arguments)
Funktionsliterale
Funktionen höherer Ordnung (engl. higher-order functions)
Einstieg
Die gängigste Art eine Funktion zu definieren ist diese als Bestandteil eines Objektes zu definieren. Derartige Funktionen werden auch als Methode bezeichnet1. Da Scala eine objektorientierte Sprache ist, sind Funktionen, welche nicht an ein Objekt gebunden sind (sie sind dementsprechend keine Methode) selber wiederum Objekte.
Bedingungen, die eine Funktion erfüllen muss um als Funktion zu gelten sind:
- Eine Funktion ist frei von Seiteneffekten
- Eine Funktion hat ein Ergebnis
- Eine Funktion liefert für gleiche Argumente immer das gleiche Ergebnis
Die Deklaration einer Funktion beginnt mit dem Schlüsselwort def
gefolgt vom Namen der Funktion. Nach dem Namen folgt in Runden Klammern die Parameterliste
der Funktion.
Hinter den Runden Klammern wird nach einem Doppelpunkt der Rückgabetyp
der Funktion definiert. Abschließend folgt nach einem Gleichheitszeichen
der Inhalt der Funktion, welcher bei mehreren Anweisungen durch geschweifte Klammern
zu einer Blockanweisung zusammengefasst wird.
def test(v: Int):Int={
println(v)
return v+1
}
In Scala braucht das return Statement nur angegeben zu werden,
wenn ohne diese Anweisung weiter Code ausgeführt würde. Der
Rückgabewert einer Funktion ist immer das Ergebnis des zuletzt ausgeführten
Ausdrucks. Demnach ist die nachfolgend aufgeführte Funktion test2
equivalent zur vorherigen Funktion test.
def test2(v: Int):Int={
println(v)
v+1
}
Besteht eine Funktion nur aus einer Anweisung, können wir die geschweiften Klammern des Methodenrumpfs auch weglassen. Dadurch lassen sich einfache Funktionen lesbarer und in kürzerer Schreibweise darstellen.
def simpleFunction(v1: Int, v2: Int) : Int = (v1 + v2) * 3
In Scala hat jede Funktion einen Rückgabewert. Funktionen, deren Rückgabewert vom Typ () "Unit" ist, werden auch als Prozedur (engl. procedure) bezeichnet. Der einzige Sinn des Aufrufs einer Prozedur ist die Ausführung von Seiteneffekten. Prozeduren sind demnach nicht "funktional programmiert".
Funktionen, die andere Funktionen als Argument erwarten oder als Rückgabewert haben, werden als Funktionen höherer Ordnung (engl. higher-order functions) bezeichnet.
Methoden / Funktionsaufruf
Ist eine Funktion an ein Objekt gebunden (also eine Methode) erfolgt deren Aufruf durch Angabe des Objektes gefolgt von einem Punkt, worauf die aufzurufende Funktion angegeben wird.
objectName.methodName(argument)
Möchten wir eine Methode eines Singleton- oder Companion Objektes (dt. Begleitobjekt) aufrufen, setzen wir anstatt der Instanzvariable den Objektnamen. Dieser Objektname repräsentiert das einzig bestehende Objekt diesen Typs.
SingletonName.methodName(argument)
In Scala gilt die Regel, dass der Punkt zwischen Objekt und Methode nicht angegeben werden muss. Dies kann zu einer "natürlicheren" Schreibweise beitragen, die auch das nachträgliche Lesen vereinfacht. Die nachfolgende Aufrufart ist demnach auch zulässig:
objectName methodName(argument)
Eine weitere Regel in Scala ist, das wenn eine Methode ein oder kein Argument hat, dann kann die dem Argument umgebene Klammer weggelassen werden. Enthält eine Methodensignatur mehr als ein Argument, kann zwar der Punkt weggelassen werden, aber die Klammern müssen angegeben werden.
objectName methodName argument
Die zuletzt beschriebene Aufrufweise wird als Operatorschreibweise bezeichnet. Tatsächlich gibt es in Scala keine Operatoren. Sprachkonstrukte, die wie Operatorenaufrufe aussehen, sind Methoden-/Funktionsaufrufe in Operatorschreibweise.
Setter und Getter
Zum Einstieg zu Setter und Getter in Scala sehen wir uns zunächst folgenden Quelltext an.
class A {
var b = new B()
println(b.value)
b.value = "321"
println(b.value)
}
class B (){
var value = "123"
}
timpt.de - X2H V 0.9
Es hat den Anschein als würde in Klasse A direkt auf das var value
zugegriffen, was aber nicht der Fall ist. Der Scala Compiler hat automatisch
einen Setter und einen Getter für value generiert.
Die Getter-Methode hat den gleichen Namen wie die zugeordnete Variable. Die Setter-Methode hat im obigen Beispiel folgende Form:
def value_=(s: String)
Das Vorhandensein des Setters kann verdeutlicht werden, indem wir ihn explizit definieren:
class A {
var b = new B()
println(b.value)
b.value = "321"
println(b.value)
}
class B (){
var value = "123"
def value_=(s: String)
}
timpt.de - X2H V 0.9
Versuchen wir diesen Quelltext zu kompilieren erhalten wir folgende Fehlermeldung:
C:\test>scalac Test.scala
A.scala:4: error: ambiguous reference to overloaded definition,
both method value_= in class B of type (x$1: java.lang.String)Unit
and method value_= in class B of type (s: String)Unit
match argument types (java.lang.String)
b.value = "321"
^
A.scala:11: error: method value_= is defined twice
def value_=(s: String)
^
two errors found
In vielen Fällen möchten wir jedoch beim Aufruf eines Setters oder
Getters eingreifen und weitere Aktionen veranlassen. Für diesen Fall
gehen wir einfach wie folgt vor. Wir definieren zunächst eine Variable,
welchen den Wert speichern soll. Im nächsten Schritt definieren wir den
Setter und den Getter in der Form, wie diese vorher automatisch generiert wurden.
Ein weiteres Eingreifen an anderen Stellen des Quelltextes eines Programms
ist nicht notwendig, da die Setter und Getter genauso vorliegen, wie es
bei den automatisch generierten der Fall war. Im nachfolgenden Beispiel
ist in Klasse B die beschriebene Vorgehensweise angewendet worden.
class A {
var b = new B()
println(b.value)
b.value = "321"
println(b.value)
}
class B (){
private[this] var myValue = "123"
def value_= (s: String){
println("setter Method called")
myValue = value
}
def value : String = myValue
}
timpt.de - X2H V 0.10
Variable Parameterzahl / wiederholte Parameter
Der letzte (oder einzige) Parameter der Parameterliste einer Funktion kann als wiederholbar mit variabler Länge gekennzeichnet werden. Dies erlaubt, dass dieser Parameter einmal, keinmal oder beliebig oft im Funktionsaufruf angegeben werden kann. Die Kennzeichnung erfolgt, indem wir dem Parametertyp ein Asterisk (Sternchen) anhängen. Innerhalb der Funktion greifen wir auf den entsprechenden Parameter über ein Array des entsprechenden Typs zu. Das nachfolgende Beispiel zeigt zwei Funktionen mit variabler Parameterliste.
object MainObject {
def main(args: Array[String]) {
val a = new A()
a.demo1(1,2,3,4,5)
a.demo1(6)
a.demo2()
a.demo2(7,8)
a.demo2(9,10,11)
}
}
class A{
def demo1(value1: Int, value2: Int*) : Unit = {
println(value1)
value2.foreach(println)
}
def demo2(value1: Int*) : Unit = value1.foreach(println)
}
timpt.de - X2H V 0.11
Die Ausgabe des Programmes liefert:
1
2
3
4
5
6
7
8
9
10
11
Auch wenn wir innerhalb der Funktion auf die variablen Parameter über
ein Array zugreifen, können wir der Funktion kein Array (oder Seq z.B. List)
direkt übergeben. Um den Compiler mitzuteilen, jedes Element einer Seq
oder eines Arrays als einzelne Parameter zu verwenden, verwenden wir die
Syntax: variablenName: _* in der Parameterliste.
Das nachfolgende Beispiel zeigt die Vorgehensweise:
object MainObject {
def main(args: Array[String]) {
val a = new A()
val arr: Array[Int] = Array(1,2,3)
val list: List[Int] = List(4,5,6)
a.demo1(0,arr: _*)
a.demo2(list: _*)
}
}
class A{
def demo1(value1: Int, value2: Int*) : Unit = {
println(value1)
value2.foreach(println)
}
def demo2(value1: Int*) : Unit = value1.foreach(println)
}
timpt.de - X2H V 0.11
Die Ausführung des Programmes führt zu folgender Ausgabe auf der Systemausgabe:
0
1
2
3
4
5
6
Benannte Argumente (engl. named arguments)
In Scala können die Argumente einer Funktion, wie bei Konstruktoren, über Ihren Namen übergeben werden. Werden die Argumente mit Ihrem Namen übergeben, ist die Reihenfolge, in der die Argumente angegeben werden, nicht mehr von Bedeutung. Das nachfolgende Beispiel zeigt die Anwendung benannter Argumente.
object NamedArguments {
def main(args: Array[String]) {
doOutput(1,2)
doOutput(x = 1, y = 2)
doOutput(y = 1, x = 2)
}
def doOutput(x: Int, y: Int) : Unit = {
println("x = " + x)
println("y = " + y)
}
}
timpt.de - X2H V 0.10
Der Ablauf des Programmes führt zu folgender Ausgabe auf der Systemausgabe:
x = 1
y = 2
x = 1
y = 2
x = 2
y = 1
Es ist auch möglich, eine Mischung aus Parameterangabe nach Parameterreihenfolge und benannten Argumenten zu machen. Wurde jedoch ein Argument benannt übergeben, müssen alle weiteren Parameter benannt übergeben werden. Das nachfolgende Programm zeigt beispielhaft diese Möglichkeit.
object NamedArguments2 {
def main(args : Array[String]) {
doOutput(1,z = 2,y = 3)
}
def doOutput(x: Int, y: Int, z: Int) {
println("x = "+x)
println("y = "+y)
println("z = "+z)
}
}
timpt.de - X2H V 0.10
Die Ausführung des Programmes führt zu folgender Ausgabe auf der Systemausgabe:
x = 1
y = 3
z = 2
Standardargumente (engl. default arguments)
In Scala ist es möglich für Argumente einer Funktion Standardwerte, wie bei Konstruktoren, festzulegen. Wird ein Argument beim Aufruf einer Funktion nicht angegeben, so wird der Standardwert verwendet. Das nachfolgende Beispiel gibt einen ersten Eindruck zur Verwendung von Standardargumenten.
object StandardArgumente {
def main(args: Array[String]) {
standard1()
standard1(3)
standard1(4,5.0)
}
def standard1(v1: Int = 1, v2: Double = 2.0) {
println(v1+" "+v2)
}
}
timpt.de - X2H V 0.10
Auch wenn man Argumente mit Standardwerten verwendet, müssen nicht alle Argumente mit Standardwerten belegt werden. Es ist problemlos möglich, Argumente mit Standardwerten und Argumente ohne Standardwerte zu vermischen. Findet eine Mischung statt, so ist darauf zu achten, dass die Standardargumente möglichst am Ende der Argumentenliste definiert werden. Ist dies nicht der Fall, kommen die Standardwerte (außer beim Einsatz benannter Argumente) nicht zum Einsatz.
object StandardArgumente2 {
def main(args: Array[String]) {
standard2(1)
standard2(2,3.0)
standard2(3,4.0,false)
}
def standard2(arg1: Int, arg2: Double = 2.0, arg3: Boolean = true) {
println(arg1+" "+arg2+" "+arg3)
}
}
timpt.de - X2H V 0.10
Funktionsliterale
Funktionen müssen nicht unbedingt an ein Objekt gebunden sein. Funktionen können auch direkt an der Stelle definiert werden, an denen Sie im Quelltext gebraucht werden. Derartige Funktionen sind Funktionsliterale in der Form2:
(x: Int, y: Int) => x + y

Funktionen höherer Ordnung (engl. higher-order functions)
Funktionen sind in Scala vollwertige Elemente. Da Scala eine "vollständig" objektorientierte Sprache ist, sind Funktionen in Scala Objekte. Funktionen können unter anderem
- An andere Funktionen übergeben werden
- Rückgabe einer anderen Funktion sein
- An Variable gebunden werden
Nachfolgend ein Beispiel einer höheren Funktion, welche eine Funktion als Argument in der Parameterliste erwartet.
def higherOrder(myFunction: (Int) => Int) = {}
timpt.de - X2H V 0.10
Eingeleitet wird diese Funktion höherer Ordnung wie gewohnt mit dem Schlüsselwort def
gefolgt vom Namen der Funktion higherOrder. In den Klammern hinter dem Namen
folgt als Parameter eine Funktionsdefinition myFunction: (Int) => Int.
Hierbei ist myFunction der Name der Funktion, wie Sie innerhalb von higherOrder
angesprochen werden kann. Wie bei Variablen muss der Name der Funktion nicht mit dem Namen
der übergebenden Funktion übereinstimmen. Nun folgt in Klammern die Parametersignatur-Definition,
welche die übergebene Funktion aufweisen muss. Nach dem recht gerichteten Pfeil =>
folgt die Ergebnisdefinition, welche mit dem Ergebnistyp der übergebenen Funktion übereinstimmen muss.
Das Gleichheitszeichen und die geschweiften Klammern runden das Beispiel zu einer vollständigen
Funktionsdefinition ab.
Im nachfolgenden Beispiel wollen wir unsere Funktion derart erweitern, dass mit der übergebenen Funktion innerhalb der Funktion höheren Ordnung etwas geschieht. Im Beispiel geben wir einfach das Ergebnis der übergebenen Funktion auf der Systemausgabe aus.
def higherOrder(myFunction: (Int) => Int) {
println(myFunction(3))
}
timpt.de - X2H V 0.11
Und nun noch ein vollständiges Programm, wo zwei unterschiedliche Funktionen unserer Funktion höherer Ordnung übergeben werden. ü>
object GettingHigher {
def main(args:Array[String]) {
def function1 = (x: Int) => 3 * x
def function2 = (y: Int) => 4 * y
higherOrder(function1)
higherOrder(function2)
}
def higherOrder(myFunction: (Int) => Int) {
println(myFunction(3))
}
}
timpt.de - X2H V 0.11
Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:
9
12
Besteht die Parameterliste der zu übergebenen Funktion aus nur einem Parameter, so kann die Klammer um den Parameter auch weggelassen werden.
def higherOrder(myFunction: Int => Int) = {}
timpt.de - X2H V 0.10
Enthält die Parametersignatur der zu übergebenen Function mehrere Parameter, so sind diese einfach durch ein Komma getrennt aufzulisten.
def higherOrder(myFunction: (Int,Double,String) => Int) = {}
timpt.de - X2H V 0.10
1 Siehe [38] Seite 139 und [39] Seite 25
2 Siehe [38] Seite 34 - Figure 2.2 The syntax of a function literal in Scala