Tour of Scala

Kompozycja domieszek

Language

W przeciwieństwie do języków, które wspierają jedynie pojedyncze dziedziczenie, Scala posiada bardziej uogólniony mechanizm ponownego wykorzystania klas. Scala umożliwia wykorzystanie nowych elementów klasy (różnicy w stosunku do klasy bazowej) w definicji nowej klasy. Wyraża się to przy pomocy kompozycji domieszek.

Rozważmy poniższe uogólnienie dla iteratorów:

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

Następnie rozważmy klasę domieszkową, która doda do klasy AbsIterator metodę foreach wykonującą podaną funkcję dla każdego elementu zwracanego przez iterator. Aby zdefiniować klasę domieszkową, użyjemy słowa kluczowego trait:

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

Oto przykład konkretnego iteratora, który zwraca kolejne znaki w podanym łańcuchu znaków:

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 }
}

Chcielibyśmy także połączyć funkcjonalność StringIterator oraz RichIterator w jednej klasie. Z pojedynczym dziedziczeniem czy też samymi interfejsami jest to niemożliwe, gdyż obie klasy zawierają implementacje metod. Scala pozwala na rozwiązanie tego problemu z użyciem kompozycji domieszek. Umożliwia ona ponowne wykorzystanie różnicy definicji klas, tzn. wszystkich definicji, które nie zostały odziedziczone. Ten mechanizm pozwala nam na połączenie StringIterator z RichIterator, tak jak w poniższym przykładzie - gdzie chcielibyśmy wypisać w kolumnie wszystkie znaki z danego łańcucha:

object StringIteratorTest {
  def main(args: Array[String]) {
    class Iter extends StringIterator("Scala") with RichIterator
    val iter = new Iter
    iter foreach println
  }
}

Klasa iter w funkcji main jest skonstruowana wykorzystując kompozycję domieszek StringIterator oraz RichIterator z użyciem słowa kluczowego with. Pierwszy rodzic jest nazywany klasą bazową Iter, podczas gdy drugi (i każdy kolejny) rodzic jest nazywany domieszką.