5.11. Dziedziczenie
Klasy mogą dziedziczyć po innych klasach. W celu określenia, że klasa A dziedziczy po klasie B, w definicji klasy A należy dodać słowo kluczowe extends, a po nim nazwę klasy B i opcjonalnie, w nawiasach, wartości parametrów, które mają być przekazane do konstruktora tej klasy. Jeśli klasa zawiera ciało, to klauzulę extends należy dodać przed ciałem klasy.
Plik Extending1.scala: class Greet(hello: String, name: String) { def greet = println(hello + " " + name) } class GreetPeter(hello: String) extends Greet(hello, "Peter") class HiPeter extends GreetPeter("Hi")
Plik Extending1.scala zawiera trzy klasy, które tworzą hierarchię, w której klasa HiPeter dziedziczy po GreetPeter, a ta z kolei dziedziczy po Greet.
scala> var peter: GreetPeter = new GreetPeter("Hello") peter: GreetPeter = GreetPeter@11c693d scala> peter.greet Hello Peter scala> peter = new HiPeter peter: GreetPeter = HiPeter@76f6a0 scala> peter.greet Hi Peter scala> peter = new Greet("Hello", "John") <console>:11: error: type mismatch; found : Greet required: GreetPeter peter = new Greet("Hello", "John") ^
Powyższe przykłady ilustrują użycie tych klas. Zmiennej peter, typu GreetPeter, przypisujemy najpierw instancję klasy GreetPeter, a potem klasy HiPeter. Kompilator dopuszcza przypisanie instancji klasy HiPeter do zmiennej typu GreetPeter, gdyż HiPeter jest podklasą klasy GreetPeter. Natomiast próba przypisania do tej samej zmiennej obiektu typu Greet nie udaje się. Klasa Greet nie jest ani klasą GreetPeter, ani jej podklasą.
Klasy, które dziedziczą po innych klasach, mogą nadpisywać składowe zdefiniowane w nadklasie, a także mogą dodawać nowe składowe. Nadpisywanie nieabstrakcyjnych składowych wymaga użycia modyfikatora override.
Plik Extending2.scala: class Hello2 { def speak: String = "Hello" } class Hi2 extends Hello2 { override def speak: String = "Hi" def hi: String = speak } class Speak2(override val speak: String) extends Hello2
Klasa Hi2 z pliku Extending2.scala nadpisuje metodę speak i dodaje nową metodę hi. Metoda speak ma modyfikator override, a metoda hi go nie ma.
scala> val a = new Hello2 a: Hello2 = Hello2@1f4357f scala> a.speak res2: String = Hello scala> val b = new Hi2 b: Hi2 = Hi2@1202a3a scala> b.speak res3: String = Hi scala> b.hi res4: String = Hi
Próba nadpisania metody speak z pominięciem modyfikatora override zakończyłaby się błędem kompilacji.
Plik Extending3.scala: class Hello3 { def speak = println("Hello") } class Hi3 extends Hello3 { def speak = println("Hi") def hi = speak }
Plik Extending3.scala zawiera taką błędną definicję klasy.
$ scalac Extending3.scala Extending3.scala:3: error: overriding method speak in class Hello3 of type => Unit; method speak needs `override' modifier def speak = println("Hi") ^ one error found
Dodanie modyfikatora override do definicji metody hi, tak jak to jest zrobione w pliku Extending4.scala, również powoduje błąd kompilacji.
Plik Extending4.scala: class Hello4 { def speak = println("Hello") } class Hi4 extends Hello4 { override def speak = println("Hi") override def hi = speak }
Błąd jest spowodowany tym, że metoda hi nie nadpisuje niczego.
$ scalac Extending4.scala Extending4.scala:4: error: method hi overrides nothing override def hi = speak ^ one error found
Jeśli klasa utworzona w języku Scala nie dziedziczy jawnie z jakiejś innej klasy, to niejawnie dziedziczy z klasy AnyRef. W pliku HelloName.scala znajduje się definicja klasy HelloName, która niejawnie dziedziczy po AnyRef.
Plik HelloName.scala: class HelloName(name: String) { def hello = println("Hello "+name+"!") override def toString = "HelloName("+name+")" }
Klasa HelloName nadpisuje metodę toString odziedziczoną z klasy AnyRef. Zauważmy, że pokazana przez konsolę tekstowa reprezentacja instancji utworzonej w poniższym przykładzie odpowiada temu, co zwraca metoda toString wywołana na tej instancji.
scala> val paul = new HelloName("Paul") paul: HelloName = HelloName(Paul) scala> paul.toString res0: String = HelloName(Paul) scala> paul.hello Hello Paul!
Nie jest możliwe utworzenie bezpośredniej podklasy klasy Any.
scala> class A extends Any <console>:10: error: Any does not have a constructor class A extends Any ^