-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path08_Traits.sc
230 lines (194 loc) · 5.26 KB
/
08_Traits.sc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// 8.1 Using a Trait as an Interface
trait BaseSoundPlayer {
def play
def close
def pause
def stop
def resume
}
trait Dog {
def speak(whatToSay: String)
def wagTail(enabled: Boolean)
}
class Mp3SoundPlayer extends BaseSoundPlayer {
def play
def close
def pause
def stop
def resume
}
// must be declared abstract because it does not implement
// all of the BaseSoundPlayer methods
abstract class SimpleSoundPlayer extends BaseSoundPlayer {
def play { /*...*/ }
def close { /*...*/ }
}
trait Mp3BaseSoundFilePlayer extends BaseSoundFilePlayer {
def getBasicPlayer: BasicPlayer
def getBasicController: BasicController
def setGain(volume: Double)
}
// 8.2 Using Abstract and Concrete Fields in Traits
trait PizzaTrait {
var numToppings: Int // abstract
var size = 14 // concrete
val maxNumToppings = 10 // concrete
}
class Pizza extends PizzaTrait {
var numToppings = 0 // 'override' not needed
size = 16 // 'var' and 'override' not needed
override val maxNumToppings = 10 // 'override' is required
}
// 8.3 Using a Trait Like an Abstract Class
{
trait Pet {
// > Avoid the procedure syntax, as it tends to be confusing for very little gain in brevity.
// https://docs.scala-lang.org/style/declarations.html
def speak: Unit = { println("Yo") } // concrete implementation
def comeToMaster: Unit // abstract method
}
class Dog extends Pet {
// don't need to implement 'speak' if you don't need to
def comeToMaster: Unit = { println("I'm coming!") }
}
class Cat extends Pet {
// override the speak method
override def speak: Unit = { println("meow") }
def comeToMaster: Unit = { println("That's not gonna happen.") }
}
abstract class FlyingPet extends Pet {
def fly: Unit = { println("I'm flying!") }
}
}
// 8.4 Using Traits as Simple Mixins
{
trait Tail {
def wagTail { println("tail is wagging") }
def stopTail { println("tail is stopped") }
}
abstract class Pet(var name: String) {
def speak // abstract
def ownerIsHome { println("excited") }
def jumpForJoy { println("jumping for joy") }
}
class Dog(name: String) extends Pet(name) with Tail {
def speak { println("woof") }
override def ownerIsHome {
wagTail
speak
}
}
object Test extends App {
val zeus = new Dog("Zeus")
zeus.ownerIsHome
zeus.jumpForJoy
}
Test.main(Array())
}
// 8.5 Limiting Which Classes Can Use a Trait by Inheritance
{
class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class Starship extends StarfleetComponent with StarfleetWarpCore
}
{
class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class RomulanStuff
// won't compile
class Warbird extends RomulanStuff with StarfleetWarpCore
}
{
abstract class Employee
class CorporateEmployee extends Employee
class StoreEmployee extends Employee
trait DeliversFood extends StoreEmployee
// this is allowed
class DeliveryPerson extends StoreEmployee with DeliversFood
// won't compile
class Receptionist extends CorporateEmployee with DeliversFood
}
// 8.6 Marking Traits So They Can Only Be Used by Subclasses of a Certain Type
{
trait StarfleetWarpCore { this: Starship =>
// more code here ...
}
class Starship
class Enterprise extends Starship with StarfleetWarpCore
class RomulanShip
// this won't compile
class Warbird extends RomulanShip with StarfleetWarpCore
}
{
trait WarpCore {
this: Starship with WarpCoreEjector with FireExtinguisher =>
}
class Starship
trait WarpCoreEjector
trait FireExtinguisher
// this works
class Enterprise extends Starship with WarpCore with WarpCoreEjector with FireExtinguisher
}
// 8.7 Ensuring a Trait Can Only Be Added to a Type That Has a Specific Method
{
trait WarpCore {
this: { def ejectWarpCore(password: String): Boolean } =>
}
class Starship {
// code here ...
}
class Enterprise extends Starship with WarpCore {
def ejectWarpCore(password: String): Boolean = {
if (password == "password") { println("ejecting core"); true } else false
}
}
}
{
trait WarpCore {
this: {
def ejectWarpCore(password: String): Boolean
def startWarpCore: Unit
} =>
}
class Starship
class Enterprise extends Starship with WarpCore {
def ejectWarpCore(password: String): Boolean = {
if (password == "password") { println("core ejected"); true } else false
}
def startWarpCore { println("core started") }
}
}
// 8.8 Adding a Trait to an Object Instance
{
class DavidBanner
trait Angry {
println("You won't like me ...")
}
object Test extends App {
val hulk = new DavidBanner with Angry
}
Test.main(Array())
}
{
trait Debugger {
def log(message: String) {
// do something with message
}
}
// no debugger
val child = new Child
// debugger added as the object is created
val problemChild = new ProblemChild with Debugger
}
// 8.9 Extending a Java Interface Like a Trait
```java
public interface Animal { public void speak(); }
public interface Wagging { public void wag(); }
public interface Running { public void run(); }
```
// scala
class Dog extends Animal with Wagging with Running {
def speak { println("Woof") }
def wag { println("Tail is wagging!") }
def run { println("I'm running!") }
}