Na tej stronie omówimy podstawy języka Scala.
Uruchamianie Scali w przeglądarce
Dzięki Scastie możesz uruchomić Scalę w swojej przeglądarce.
- Przejdź do Scastie.
- Wklej kod
println("Hello, world!")
w polu po lewej stronie. - Naciśnij przycisk “Run”. W panelu po prawej stronie pojawi się wynik działania programu.
Jest to prosta i niewymagająca żadnej instalacji metoda do eksperymentowania z kodem w Scali.
Wiele przykładów kodu w tym przewodniku jest również zintegrowana ze Scastie, dzięki czemu można je wypróbować wciskając po prostu przycisk “Run”.
Wyrażenia
Wyrażenia są rezultatem ewaluacji fragmentów kodu.
1 + 1
Wyniki wyrażeń można wyświetlić za pomocą funkcji println
.
println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!
Wartości
Rezultaty wyrażeń mogą zostać nazwane za pomocą słowa kluczowego val
.
val x = 1 + 1
println(x) // 2
Nazwane wyniki, tak jak x
w ww. przykładzie, to wartości.
Odniesienie się do wartości nie powoduje jej ponownego obliczenia.
Wartości nie można przypisać ponownie.
x = 3 // Nie kompiluje się.
Typy wartości mogą być wywnioskowane przez kompilator, ale można również wyraźnie określić type:
val x: Int = 1 + 1
Zauważ, że deklaracja Int
pojawia po identyfikatorze x
, potrzebny jest rówmież dwukropek :
.
Zmienne
Zmienne są podobne do wartości, ale z tym wyjątkiem, że można je ponownie przypisywać.
Zmienną można zdefiniować używając słowa kluczowego var
.
var x = 1 + 1
x = 3 // Kompiluje się, ponieważ "x" jest zdefiniowane z użyciem "var".
println(x * x) // 9
Tak jak przy wartościach, można wyraźnie zdefiniować żądany typ:
var x: Int = 1 + 1
Wyrażenia blokowe
Wyrażenia mogą być łączone poprzez zamknięcie ich w nawiasie klamrowym{}
.
Taką konstrukcję nazywamy blokiem.
Wynikiem całego bloku kodu jest wynik ostatniego wyrażenia w tym bloku.
println({
val x = 1 + 1
x + 1
}) // 3
Funkcje
Funkcje to wyrażenia, które przyjmują pewne parametry.
Poniżej zdefiniowana jest funkcja anonimowa (nieposiadająca nazwy), która zwraca liczbę całkowitą przekazaną jako parametr, zwiększoną o 1.
(x: Int) => x + 1
Po lewej stronie od =>
znajduje się lista parametrów.
Po prawej stronie - wyrażenie wykorzystujące te parametry.
Funkcje można również nazywać.
val addOne = (x: Int) => x + 1
println(addOne(1)) // 2
Funkcje mogą przyjmować wiele parametrów.
val add = (x: Int, y: Int) => x + y
println(add(1, 2)) // 3
Mogą też wcale nie mieć parametrow.
val getTheAnswer = () => 42
println(getTheAnswer()) // 42
Metody
Metody wyglądają i zachowują się bardzo podobnie jak funkcje, jednak jest między nimi kilka kluczowych różnic.
Metody są definiowane z użyciem słowa kluczowego def
.
Po def
następuje nazwa metody, lista parametrów, zwracany typ i ciało metody.
def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3
Zauważ, że zwracany typ jest zadeklarowany po liście parametrów i dwukropku : Int
.
Metody mogą mieć wiele list parametrów.
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9
Mogą również wcale ich nie posiadać.
def name: String = System.getProperty("user.name")
println("Hello, " + name + "!")
Od funkcji odróżnia je jeszcze kilka innych rzeczy, ale na razie możesz o nich myśleć jak o bardzo podobnych do funkcji.
Metody mogą zawierać również wyrażenia wielowierszowe.
def getSquareString(input: Double): String = {
val square = input * input
square.toString
}
println(getSquareString(2.5)) // 6.25
Ostatnie wyrażenie w ciele metody jest wartością, jaką zwraca cała metoda.
Scala posiada słowo kluczowe return
, ale jest ono wykorzystywane bardzo rzadko.
Klasy
Klasy są definiowane za pomocą słowa kluczowego class
, po którym następuje nazwa klasy i parametry konstruktora.
class Greeter(prefix: String, suffix: String) {
def greet(name: String): Unit =
println(prefix + name + suffix)
}
Metoda greet
zwraca typ Unit
- oznacza to, że nie ma nic znaczącego do zwrócenia.
Unit
jest używany podobnie jak void
w językach Java i C.
Różnica polega na tym, że w Scali każde wyrażenie musi zwracać jakąś wartosć, tak naprawdę istnieje obiekt singleton typu Unit
- nie niesie on ze sobą żadnej znaczącej informacji.
Nowe instancje klasy tworzy się za pomocą słowa kluczowego new
.
val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!
Klasy zostaną szerzej omówione w dalszej części tego przewodnika.
Klasy przypadku (case classes)
W Scali istnieje spacjalny typ klasy - klasa “przypadku” (case class).
Klasy przypadku są domyślnie niezmienne i porównywane przez wartości.
Klasy te można definiować używająć słów kluczowych case class
.
case class Point(x: Int, y: Int)
Do utworzenia nowej instacji klasy przypadku nie jest konieczne używanie słowa kluczowego new
.
val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)
Są one porównywane przez wartości - nie przez referencje.
if (point == anotherPoint) {
println(s"$point i $anotherPoint są jednakowe.")
} else {
println(s"$point i $anotherPoint są inne.")
} // Point(1,2) i Point(1,2) są jednakowe.
if (point == yetAnotherPoint) {
println(s"$point i $yetAnotherPoint są jednakowe.")
} else {
println(s"$point i $yetAnotherPoint są inne.")
} // Point(1,2) i Point(2,2) są inne.
Klasy przypadków to dużo szerszy temat, do zapoznania z którym bardzo zachęcamy. Jesteśmy pewni, że Ci się spodoba! Jest on dokładnie omówiony w późniejszym rozdziale.
Obiekty
Obiekty to pojedyncze wystąpienia ich definicji. Można o nich myśleć jak o instancjach ich własnych klas - singletonach.
Objekty definiuje się z użyciem słowa kluczowego object
.
object IdFactory {
private var counter = 0
def create(): Int = {
counter += 1
counter
}
}
Aby uzyskać dostęp do obiektu używa się jego nazwy.
val newId: Int = IdFactory.create()
println(newId) // 1
val newerId: Int = IdFactory.create()
println(newerId) // 2
Obiekty zostaną szerzej omówione później.
Cechy (traits)
Cechy to typy zawierające pewne pola i metody. Wiele cech może być łączonych.
Cechę (trait) można zdefiniować używając słowa kluczowego trait
.
trait Greeter {
def greet(name: String): Unit
}
Cechy mogą zawierać domyślną implementację.
trait Greeter {
def greet(name: String): Unit =
println("Hello, " + name + "!")
}
Cechy można rozszerzać używając słowa kluczowego extends
i nadpisać implementację z użyciem override
.
class DefaultGreeter extends Greeter
class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
override def greet(name: String): Unit = {
println(prefix + name + postfix)
}
}
val greeter = new DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!
val customGreeter = new CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?
W tym przykładzie DefaultGreeter
rozszerza tylko jedną cechę (trait), ale równie dobrze może rozszerzać ich wiele.
Cechy zostały dokładniej opisane w jednym z kolejnych rozdziałów.
Metoda Main
Metoda main
to punkt wejścia do programu.
Maszyna Wirtalna Javy (Java Virtual Machine / JVM) wymaga, aby metoda ta nazywała się “main” i posiadała jeden arguemnt - tablicę ciągów znaków.
Z użyciem obiektu można zdefiniować metodę main
w następujący sposób:
object Main {
def main(args: Array[String]): Unit =
println("Hello, Scala developer!")
}