Scala w Notatkach #2 - zmienne i funkcje

W Scali mamy dwa rodzaje zmiennych deklarowane odpowiednio słowami: val oraz var. Pierwsze słowo kluczowe oznacza zmienną niezmienną, drugie zmienną zmienną. ;) Wyszło trochę śmiesznie, dlatego przekładając to na nasze można powiedzieć, że val to takie Javove final, zaś var to, coś onego final pozbawonie.

Przykładowa deklaracja może wyglądać tak:

package com.jcake.scala
object Application {
        def main(args:Array[String]) {
                val a = "Przyklad zmiennej niezmiennej"
                return;
        }
}

Już na pierwszy rzut oka widać, że nie coś jest nie tak. Zmienna a nie ma zadeklarowanego typu! To nic - i tak zadziała, ponieważ scala jest całkiem niezła w odgadywaniu typów za nas. Wszędzie tam, gdzie kompilator potrafi sam wywnioskować typ, możemy go spokojnie pominąć. A jeśli nie chcemy niczego pominąć? Wtedy deklaracja powinna wyglądać tak:

     val a: String = "Przyklad zmiennej niezmiennej"

albo

     val a: java.lang.String = "Przyklad zmiennej niezmiennej"

Utworzona zmienna a jest final, czyli ponowne przypisanie do niej wartości spowoduje błąd kompilacji. Jeśli chcielibyśmy utworzyć zmienną, której przypisanie chcielibyśmy kiedyś zmienić to zamiast val zastosowalibyśmy var. Reszta pozostaje bez zmian.

Ale zaraz, zaraz... dlaczego musiałem dodać return do metody main? Zobaczmy, co się stanie, jeśli spróbujemy skompilować poniższy kod:

package com.jcake.scala
object Application {
        def main(args:Array[String]) {
                val a = "Przyklad zmiennej niezmiennej"
        }
}

ant@labs:/tmp$ fsc Application.scala
/tmp/Application.scala:6: error: block must end in result expression, not in definition
        }
        ^
one error found

Hmm... czyli w wolnym tłumaczeniu: blok musi kończyć się wyrażeniem, a nie definicją. Okazuje się, że ostanie wyrażenie w Scali jest zwracane z metody domyślnie. Metoda main(), jak pamiętamy z poprzedniego odcinka, nie zwraca niczego interesującego, czyli zwraca obiekt Unit. Zatem możemy zwrócić cokolwiek (np. właśnie utworzone a) albo napisać po prostu "return".

Skoro już jesteśmy przy metodach dopiszmy jedną do naszego skryptu:

package com.jcake.scala
object Application {
        def main(args:Array[String]) {
                if (args.length == 0) {
                        displayHelp();
                }
        }
 
        def displayHelp() {
                println("This is help")
        }
}

Powyższy kod jest skróconą wersją czegoś takiego:

package com.jcake.scala
object Application {
        def main(args:Array[String]): Unit = {
                if (args.length == 0) {
                        return displayHelp()
                }
                return
        }
 
        def displayHelp(): Unit = {
                return println("This is help")
        }
}

Pierwsza czerwona lampka zapala nam się po zobaczeniu:

return println("This is help")

println() w Scali, w przeciwieństwie do Javy, zwraca wartość! Jest nią Unit, a więc nic interesującego, ale jednak!

Druga czerwona lampka zapala się przy:

if (args.length == 0) {
    displayHelp();
}

w metodzie main. Metoda ta zwraca Unit, a w środku jest tylko if? Gdzie się podział jakiś return? Okazuje się, że if w Scali jest nieco podrasowany i potrafi zwracać wartości, czyli if w Scali zachowuje się dokładnie tak, jak operator a ? b : c w Javie. Jest jednym słowem bardziej funkcyjny niż ten Javowy. Zwracana z ifa wartość to ostanie wyrażenie z bloku, do którego if wszedł. Sprawdźmy to:

package com.jcake.scala
object Application {
        def main(args:Array[String]): Unit = {
                val x = if (args.length > 0) {
                                "sa argumenty"
                        } else {
                                "nie ma argumentow"
                        }
                println(x)
        }
}

Jak widać wynik ifa można przypisać bezpośrednio do zmiennej.

Do zapamiętania:

  • val - odpowiednik final,
  • var - odpowiednik non-final
  • def nazwa(parametr: Typ, parameterN: Typ) : TypZwracany = {} - tak definiujemy metody/ funkcje,
  • funkcja zwraca ostanie wyrażenie - nie trzeba więc używać return, o ile nie chcemy wyjść z metody wcześniej