9.9. Więcej o nadpisywaniu składowych
Typy składowych definiowanych w podklasie nie zawsze muszą być dokładnie takie, jak typy nadpisywanych składowych. Plik Overriding1.scala ilustruje niektóre z takich sytuacji.
Plik Override1.scala: class X1 { def a: Any = 'a def b: Any = 'b } class Y1 extends X1 { override def a: String = "a" override val b: String = "b" }
Klasa Y1 rozszerza klasę X1. W klasie X1 są zdefiniowane dwie metody typu Any. W klasie Y1 te dwie metody są nadpisywane metodą i wartością typu String, a więc typu, który znajduje się niżej w hierarchii typów od typu nadpisywanych metod. Takie definicje są poprawne i kompilują się.
scala> var x1 = new X1 x1: X1 = X1@199c6b5 scala> x1.a res0: Any = 'a scala> x1.b res1: Any = 'b scala> x1 = new Y1 x1: X1 = Y1@16c81a3 scala> x1.a res2: Any = a scala> x1.b res3: Any = b scala> val y1 = new Y1 y1: Y1 = Y1@163a52f scala> y1.a res4: String = a scala> y1.b res5: String = b
Plik Overriding2.scala ilustruje kolejny przypadek.
Plik Override2.scala: abstract class X2 { type A >: String <: Any def a: A } class Y2 extends X2 { override type A = AnyRef def a: A = 'a }
Klasa Y2 rozszerza klasę X2. W klasie X2 jest zdefiniowany parametr typu A, który ma ograniczenie górne Any i dolne String. Ponadto, w tej klasie zdefiniowana jest abstrakcyjna metoda a typu A. W klasie Y2 typ A jest definiowany jako AnyRef, a więc jako typ spełniający oba ograniczenia parametru typu A z nadklasy. Taka definicja jest prawidłowa. Dodatkowo, nadpisywana jest abstrakcyjna metoda a.
scala> (new Y2).a res6: Object = 'a scala> (new Y2).a res7: Object = 'a
Plik Overriding3.scala ilustruje jeszcze inne sytuacje, tym razem dotyczące nadpisywania aliasu typu przez inny alias typu.
Plik Override3.scala: abstract class X3 { type A <: Any type B >: String def c(a:A, b:B): Unit } abstract class Y3 extends X3 { override type A <: AnyRef override type B >: AnyRef } class Z3 extends Y3 { override type A = String override type B = Any override def c(a:String, b:Any) { println(a) println(b) } }
Klasa Y3 rozszerza klasę X3. W klasie X3 są zdefiniowane dwa aliasy typu o nazwach A i B. Alias A ma ograniczenie górne Any, a alias B ma ograniczenie dolne String. Ponadto, w tej klasie zdefiniowana jest abstrakcyjna metoda c typu Unit, przyjmująca parametry typu A i B. W klasie Y3 parametr typu A jest nadpisywany i deklarowany z ograniczeniem górnym AnyRef, a więc bardziej ograniczającym parametr z nadklasy. Z kolei parametr typu B jest nadpisywany i deklarowany z ograniczeniem dolnym AnyRef, a więc także bardziej ograniczającym parametr z nadklasy. Obie te deklaracje parametrów są prawidłowe i kompilują się. Klasa Z3 dziedziczy z Y3 i definiuje parametry A i B jako String i Any.
scala> (new Z3).c("a","b")
a
b
Plik Override1.scala:
class X1 {
def a: Any = 'a
def b: Any = 'b
}
class Y1 extends X1 {
override def a: String = "a"
override val b: String = "b"
}
