Tour of Scala

Kompozycja klas przez domieszki

Language

Domieszka (ang. mixin) to cecha (trait), która używana jest do komponowania klas.

abstract class A {
  val message: String
}
class B extends A {
  val message = "Jestem instancją klasy B"
}
trait C extends A {
  def loudMessage = message.toUpperCase()
}
class D extends B with C

val d = new D
println(d.message)  // wyświetli "Jestem instancją klasy B"
println(d.loudMessage)  // wyświetli "JESTEM INSTANCJĄ KLASY B"

Klasa D posiada nadklasę B oraz domieszkę C. Klasy mogą mieć tylko jedną nadklasę, ale wiele domieszek (używając kolejno słów kluczowych extends, a następnie with). Domieszki i nadklasy mogą posiadać tą samą nadklasę (typ bazowy).

Spójrzmy teraz na trochę ciekawszy przykład zawierający klasę abstrakcyjną.

abstract class AbsIterator {
  type T
  def hasNext: Boolean
  def next(): T
}

Klasa ta zawiera abstrakcyjny typ type T oraz standardowe metody iteracyjne hasNext i next.

class StringIterator(s: String) extends AbsIterator {
  type T = Char
  private var i = 0
  def hasNext = i < s.length
  def next() = {
    val ch = s charAt i
    i += 1
    ch
  }
}

Klasa StringIterator przyjmuje parametr typu String, może być ona użyta do iterowania po typach String (np. aby sprawdzić czy String zawiera daną literę).

Stwórzmy teraz cechę, która również rozszerza AbsIterator.

trait RichIterator extends AbsIterator {
  def foreach(f: T => Unit): Unit = while (hasNext) f(next())
}

Cecha RichIterator implementuje metodę foreach, która z kolei wywołuje przekazaną przez parametr funkcję f: T => Unit na kolejnym elemencie (f(next())) tak długo, jak dostępne są kolejne elementy (while (hasNext)). Ponieważ RichIterator jest cechą, nie musi implementować abstrakcyjnych składników klasy AbsIterator.

Spróbujmy teraz połączyć funkcjonalności StringIterator oraz RichIterator w jednej klasie.

object StringIteratorTest extends App {
  class RichStringIter extends StringIterator("Scala") with RichIterator
  val richStringIter = new RichStringIter
  richStringIter foreach println
}

Nowo powstała RichStringIter posiada StringIterator jako nadklasę oraz RichIterator jako domieszkę.

Mając do dyspozycji jedynie pojedyncze dziedziczenie, nie byli byśmy w stanie osiągnąć takiego stopnia elastyczności.

Contributors to this page: