6.6. Parametry
Kolejna różnica między klasami oraz cechami związana jest z parametrami klasy. W pliku TraitParams1.scala znajduje się definicja klasy Hi, zawierającej parametr.
Plik TraitParams1.scala: class Hi(name: String) { def hi = "Hi "+name+"!" }
Cechy nie mogą mieć parametrów.
Plik TraitParams2.scala: trait Hello(name: String) { def hello = "Hello "+name+"!" }
Definicja cechy Hello z pliku TraitParams2.scala jest niepoprawna i nie kompiluje się.
$ scalac TraitParams2.scala
TraitParams2.scala:1: error: traits or objects may not have parameters
trait Hello(name: String) {
^
one error found
W pliku TraitParams3.scala znajduje się definicja cechy Hello3, która kompiluje się bez błędów. Zamiast parametru, jej definicja zawiera abstrakcyjną składową name.
Plik TraitParams3.scala: trait Hello3 { val name: String def hello = println("Hello "+name+"!") }
Polecenie z wiersza
tworzy obiekt klasy anonimowej, rozszerzającej
cechę Hello3. Abstrakcyjna składowa name cechy Hello3 zostaje w
tym poleceniu nadpisana i uzyskuje wartość.
scala> val john = new Hello3 { val name = "John" }
john: Hello3 = $anon$1@12b107
scala> john.hello Hello John!
Zdefiniowane w pliku TraitParams4.scala klasy Hello4a i Hello4b, które rozszerzają cechę Hello3, deklarują parametr o nazwie name, który nadpisuje abstrakcyjną składową name cechy Hello3. W przypadku nadpisywania składowej abstrakcyjnej można, ale nie trzeba, użyć modyfikatora override. W definicji klasy Hello4a ten modyfikator jest pominięty, natomiast znajduje się w definicji klasy Hello4b.
Plik TraitParams4.scala: class Hello4a(val name: String) extends Hello3 class Hello4b(override val name: String) extends Hello3
Poniższe polecenia wykorzystują te klasy.
scala> new Hello4a("Peter").hello
Hello Peter!
scala> new Hello4b("Paul").hello
Hello Paul!
W pliku TraitParams5.scala znajduje się definicja cechy Hello5, w której — w odróżnieniu od cechy Hello3 — metoda println z metody hello nie odwołuje się bezpośrednio do wartości name, ale do wartości greeting, której definicja wykorzystuje wartość name.
Plik TraitParams5.scala: trait Hello5 { val name: String val greeting = "Hello "+name+"!"
def hello = println(greeting) } class Hello5Mary extends Hello5 { val name = "Mary" }
Klasa Hello5Mary rozszerza cechę Hello5.
scala> (new Hello5Mary).hello Hello null!
Metoda hello wywoływana na instancji tej klasy wyświetla null,
zamiast imienia. Dzieje się tak dlatego, że instrukcja z wiersza
jest wywoływana przed konstruktorem Hello5Mary. Wobec tego, w
momencie definiowania wartości greeting, a więc w momencie
wykonywania tej instrukcji, name ma początkową wartość
null. Docelową wartość uzyskuje dopiero w wyniku wykonania kodu
konstruktora klasy Hello5Mary.
Ten problem można rozwiązać za pomocą tak zwanej wczesnej definicji. Wczesne definicje umieszcza się w nawiasach klamrowych po słowie extends. Wczesne definicje są wykonywane przed wywołaniem konstruktora nadtypu. Plik TraitParams5a.scala zawiera definicję klasy Hello5Ann, w której jest użyta wczesna definicja zawierająca definicję wartości name.
Plik TraitParams5a.scala: class Hello5Ann extends { val name = "Ann" } with Hello5
Ponieważ wczesne definicje są wykonywane przed kodem konstruktora
nadtypu, więc w momencie wykonywania instrukcji z wiersza
name
zawiera już odpowiednią wartość.
scala> (new Hello5Ann).hello Hello Ann!
W pliku TraitParams5b.scala znajduje się inny przykład. Klasa Hello5Name jest zdefiniowana wewnątrz klasy Hello5Factory z wykorzystaniem wczesnej definicji. We wczesnej definicji występuje odwołanie do parametru klasy Hello5Factory. To odwołanie zawiera przedrostek this. Takie odwołanie jest prawidłowe, gdyż this w tym przypadku dotyczy klasy Hello5Factory. Blok kodu zawierający wczesne definicje jest ewaluowany tak, jakby był lokalnym blokiem, zawierającym definicje wartości.
Plik TraitParams5b.scala: class Hello5Factory(paramName: String) { class Hello5Name extends { val name = this.paramName } with Hello5 def create = new Hello5Name }
Poniższe wyrażenie ilustruje działanie kodu pliku TraitParams5b.scala.
scala> new Hello5Factory("Peter").create.hello
Hello Peter!
Próba przesunięcia bloku definiującego wartość name na koniec definicji klasy Hello5Name, tak jak to jest zrobione w pliku TraitParams5c.scala, skutkuje błędem kompilacji.
Plik TraitParams5c.scala: class Hello5Factory(paramName: String) { class Hello5Name extends Hello5 { val name = this.paramName } def create = new Hello5Name }
Błąd wynika z tego, że w takim przypadku przedrostek this odnosi się do definiowanej klasy, czyli klasy Hello5Name, a w tej klasie nie ma składowej o nazwie paramName.
$ scalac TraitParams5c.scala
TraitParams5c.scala:2: error: value paramName is not a member of Hello5Factory.this.Hello5Name
class Hello5Name extends Hello5 { val name = this.paramName }
^
one error found
![]() | Specyfikacja języka Scala opisuje wczesne definicje w punkcie 5.1.6. |
Plik TraitParams1.scala:
class Hi(name: String) {
def hi = "Hi "+name+"!"
}

