You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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_.
15
+
Domieszka (ang. mixin) to cecha (trait), która używana jest do komponowania klas.
16
16
17
-
Rozważmy poniższe uogólnienie dla iteratorów:
17
+
{% scalafiddle %}
18
+
```tut
19
+
abstract class A {
20
+
val message: String
21
+
}
22
+
class B extends A {
23
+
val message = "Jestem instancją klasy B"
24
+
}
25
+
trait C extends A {
26
+
def loudMessage = message.toUpperCase()
27
+
}
28
+
class D extends B with C
29
+
30
+
val d = new D
31
+
println(d.message) // wyświetli "Jestem instancją klasy B"
32
+
println(d.loudMessage) // wyświetli "JESTEM INSTANCJĄ KLASY B"
33
+
```
34
+
{% endscalafiddle %}
35
+
36
+
Klasa `D` posiada nadklasę `B` oraz domieszkę `C`.
37
+
Klasy mogą mieć tylko jedną nadklasę, ale wiele domieszek (używając kolejno słów kluczowych `extends`, a następnie `with`).
38
+
Domieszki i nadklasy mogą posiadać tą samą nadklasę (typ bazowy).
39
+
40
+
Spójrzmy teraz na trochę ciekawszy przykład zawierający klasę abstrakcyjną.
18
41
19
42
```tut
20
43
abstract class AbsIterator {
@@ -24,35 +47,44 @@ abstract class AbsIterator {
24
47
}
25
48
```
26
49
27
-
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`:
50
+
Klasa ta zawiera abstrakcyjny typ `type T` oraz standardowe metody iteracyjne `hasNext` i `next`.
28
51
29
52
```tut
30
-
trait RichIterator extends AbsIterator {
31
-
def foreach(f: T => Unit) { while (hasNext) f(next()) }
53
+
class StringIterator(s: String) extends AbsIterator {
54
+
type T = Char
55
+
private var i = 0
56
+
def hasNext = i < s.length
57
+
def next() = {
58
+
val ch = s charAt i
59
+
i += 1
60
+
ch
61
+
}
32
62
}
33
63
```
34
64
35
-
Oto przykład konkretnego iteratora, który zwraca kolejne znaki w podanym łańcuchu znaków:
65
+
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ę).
66
+
67
+
Stwórzmy teraz cechę, która również rozszerza `AbsIterator`.
36
68
37
69
```tut
38
-
class StringIterator(s: String) extends AbsIterator {
39
-
type T = Char
40
-
private var i = 0
41
-
def hasNext = i < s.length()
42
-
def next() = { val ch = s charAt i; i += 1; ch }
70
+
trait RichIterator extends AbsIterator {
71
+
def foreach(f: T => Unit): Unit = while (hasNext) f(next())
43
72
}
44
73
```
45
74
46
-
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:
75
+
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)`).
76
+
Ponieważ `RichIterator` jest cechą, nie musi implementować abstrakcyjnych składników klasy `AbsIterator`.
77
+
78
+
Spróbujmy teraz połączyć funkcjonalności `StringIterator` oraz `RichIterator` w jednej klasie.
47
79
48
80
```tut
49
-
object StringIteratorTest {
50
-
def main(args: Array[String]) {
51
-
class Iter extends StringIterator("Scala") with RichIterator
52
-
val iter = new Iter
53
-
iter foreach println
54
-
}
81
+
object StringIteratorTest extends App {
82
+
class RichStringIter extends StringIterator("Scala") with RichIterator
83
+
val richStringIter = new RichStringIter
84
+
richStringIter foreach println
55
85
}
56
86
```
57
87
58
-
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ą_.
88
+
Nowo powstała `RichStringIter` posiada `StringIterator` jako nadklasę oraz `RichIterator` jako domieszkę.
89
+
90
+
Mając do dyspozycji jedynie pojedyncze dziedziczenie, nie byli byśmy w stanie osiągnąć takiego stopnia elastyczności.
0 commit comments