Removed solutions from exercise descriptions
This commit is contained in:
parent
fccad93663
commit
27627f0ce4
|
@ -5,36 +5,4 @@ classes. Create an abstract class `WithParams` with class parameters `val i:
|
|||
Int` and `var s: String = ""`. Inherit a class `Concrete` from `WithParams`.
|
||||
Provide constructors for both that use `atomictest.Trace` to show the class
|
||||
name and arguments. Create an instance of `Concrete` and verify the order of
|
||||
the constructor calls.
|
||||
|
||||
```kotlin
|
||||
// Abstract/AbsExercise1.kt
|
||||
package abstractclasses
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
abstract class WithParams(
|
||||
val i: Int,
|
||||
var s: String = ""
|
||||
) {
|
||||
init {
|
||||
trace("WithParams $i $s")
|
||||
}
|
||||
}
|
||||
|
||||
class Concrete(i: Int, s: String):
|
||||
WithParams(i, s) {
|
||||
init {
|
||||
trace("Concrete $i $s")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Concrete(11, "One Louder")
|
||||
trace eq """
|
||||
WithParams 11 One Louder
|
||||
Concrete 11 One Louder
|
||||
"""
|
||||
}
|
||||
```
|
||||
the constructor calls.
|
|
@ -14,54 +14,4 @@ Add a function `tune()` that takes an `Instrument` parameter and plays that
|
|||
|
||||
In `main()`, create an `orchestra` which is a `List<Instrument>` containing an
|
||||
instance of each type of `Instrument`. Call `tune()` for each `Instrument` in
|
||||
the `orchestra`, and validate the results using `Trace`.
|
||||
|
||||
```kotlin
|
||||
// Abstract/AbsExercise2.kt
|
||||
package abstractclasses2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
enum class Note {
|
||||
A, B, C, D, E, F, G
|
||||
}
|
||||
|
||||
abstract class Instrument(val name: String) {
|
||||
abstract fun play(n: Note)
|
||||
}
|
||||
|
||||
open class Wind : Instrument("Wind") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name blow $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Percussion : Instrument("Percussion") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name strike $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Stringed : Instrument("Stringed") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name pluck $n")
|
||||
}
|
||||
}
|
||||
|
||||
fun tune(i: Instrument) = i.play(Note.C)
|
||||
|
||||
fun main() {
|
||||
val orchestra = listOf(
|
||||
Wind(),
|
||||
Percussion(),
|
||||
Stringed()
|
||||
)
|
||||
orchestra.forEach { tune(it) }
|
||||
trace eq """
|
||||
Wind blow C
|
||||
Percussion strike C
|
||||
Stringed pluck C
|
||||
"""
|
||||
}
|
||||
```
|
||||
the `orchestra`, and validate the results using `Trace`.
|
|
@ -4,72 +4,4 @@ Add classes to the previous example: `Bowed` inherits from `Stringed` and uses
|
|||
`bow` instead of `plucked` in the output, while `Brass` and `Reed` inherit
|
||||
from `Wind`. `Brass` and `Reed` do not override `play()`. Make the necessary
|
||||
modifications to allow each class to accept a `String` argument containing the
|
||||
class name.
|
||||
|
||||
```kotlin
|
||||
// Abstract/AbsExercise3.kt
|
||||
package abstractclasses3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
enum class Note {
|
||||
A, B, C, D, E, F, G
|
||||
}
|
||||
|
||||
abstract class Instrument(val name: String) {
|
||||
abstract fun play(n: Note)
|
||||
}
|
||||
|
||||
open class Wind(name: String? = null) :
|
||||
Instrument(name ?: "Wind") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name blow $n")
|
||||
}
|
||||
}
|
||||
|
||||
open class Percussion(name: String? = null) :
|
||||
Instrument(name ?: "Percussion") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name strike $n")
|
||||
}
|
||||
}
|
||||
|
||||
open class Stringed(name: String? = null) :
|
||||
Instrument(name ?: "Stringed") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name pluck $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Bowed : Stringed("Bowed") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name bow $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Brass : Wind("Brass")
|
||||
class Reed : Wind("Reed")
|
||||
|
||||
fun tune(i: Instrument) = i.play(Note.C)
|
||||
|
||||
fun main() {
|
||||
val orchestra = listOf(
|
||||
Wind(),
|
||||
Percussion(),
|
||||
Stringed(),
|
||||
Bowed(),
|
||||
Brass(),
|
||||
Reed()
|
||||
)
|
||||
orchestra.forEach { tune(it) }
|
||||
trace eq """
|
||||
Wind blow C
|
||||
Percussion strike C
|
||||
Stringed pluck C
|
||||
Bowed bow C
|
||||
Brass blow C
|
||||
Reed blow C
|
||||
"""
|
||||
}
|
||||
```
|
||||
class name.
|
|
@ -5,35 +5,4 @@ compare a `Trace` object to a multiline `String`. Create a global `private`
|
|||
`val trace` initialized to `Trace()`. Create a class `A`; inside its `init`,
|
||||
and call `trace(className)` to append the name of the class to `trace`.
|
||||
Inherit `B` from `A` and `C` from `B`, and give them similar `init`s. Create
|
||||
an instance of `C` to see the initialization order.
|
||||
|
||||
```kotlin
|
||||
// BaseClassInit/BCIExercise1.kt
|
||||
package baseclassinit
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
open class A {
|
||||
init {
|
||||
trace("A")
|
||||
}
|
||||
}
|
||||
|
||||
open class B : A() {
|
||||
init {
|
||||
trace("B")
|
||||
}
|
||||
}
|
||||
|
||||
class C : B() {
|
||||
init {
|
||||
trace("C")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
C()
|
||||
trace eq "A B C"
|
||||
}
|
||||
```
|
||||
an instance of `C` to see the initialization order.
|
|
@ -9,80 +9,4 @@ Create a class `Plate` and inherit `DinnerPlate` from it. Create a class
|
|||
`Custom` (as in "a cultural tradition"), and inherit `PlaceSetting` from it.
|
||||
Inside `PlaceSetting`, create properties containing `Spoon`, `Fork`, `Knife`
|
||||
and `DinnerPlate`, in that order. Create an instance of `PlaceSetting` to see
|
||||
the initialization order.
|
||||
|
||||
```kotlin
|
||||
// BaseClassInit/BCIExercise2.kt
|
||||
package baseclassinit
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
open class Plate {
|
||||
init {
|
||||
trace("Plate")
|
||||
}
|
||||
}
|
||||
|
||||
class DinnerPlate : Plate() {
|
||||
init {
|
||||
trace("DinnerPlate")
|
||||
}
|
||||
}
|
||||
|
||||
open class Utensil {
|
||||
init {
|
||||
trace("Utensil")
|
||||
}
|
||||
}
|
||||
|
||||
class Spoon : Utensil() {
|
||||
init {
|
||||
trace("Spoon")
|
||||
}
|
||||
}
|
||||
|
||||
class Fork : Utensil() {
|
||||
init {
|
||||
trace("Fork")
|
||||
}
|
||||
}
|
||||
|
||||
class Knife : Utensil() {
|
||||
init {
|
||||
trace("Knife")
|
||||
}
|
||||
}
|
||||
|
||||
open class Custom() {
|
||||
init {
|
||||
trace("Custom")
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceSetting : Custom() {
|
||||
val spoon = Spoon()
|
||||
val fork = Fork()
|
||||
val knife = Knife()
|
||||
val plate = DinnerPlate()
|
||||
init {
|
||||
trace("PlaceSetting")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
PlaceSetting()
|
||||
trace eq """
|
||||
Custom
|
||||
Utensil
|
||||
Spoon
|
||||
Utensil
|
||||
Fork
|
||||
Utensil
|
||||
Knife
|
||||
Plate
|
||||
DinnerPlate
|
||||
PlaceSetting
|
||||
"""
|
||||
}
|
||||
```
|
||||
the initialization order.
|
|
@ -2,81 +2,4 @@
|
|||
|
||||
Modify the previous exercise so that every class has a class argument of `i:
|
||||
Int`, and add this to the end of the `String` in the call to `trace()` inside
|
||||
each `init`.
|
||||
|
||||
```kotlin
|
||||
// BaseClassInit/BCIExercise3.kt
|
||||
package baseclassinit
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
open class Plate1(i: Int) {
|
||||
init {
|
||||
trace("Plate $i")
|
||||
}
|
||||
}
|
||||
|
||||
class DinnerPlate1(i: Int) : Plate1(i) {
|
||||
init {
|
||||
trace("DinnerPlate $i")
|
||||
}
|
||||
}
|
||||
|
||||
open class Utensil1(i: Int) {
|
||||
init {
|
||||
trace("Utensil $i")
|
||||
}
|
||||
}
|
||||
|
||||
class Spoon1(i: Int) : Utensil1(i) {
|
||||
init {
|
||||
trace("Spoon $i")
|
||||
}
|
||||
}
|
||||
|
||||
class Fork1(i: Int) : Utensil1(i) {
|
||||
init {
|
||||
trace("Fork $i")
|
||||
}
|
||||
}
|
||||
|
||||
class Knife1(i: Int) : Utensil1(i) {
|
||||
init {
|
||||
trace("Knife $i")
|
||||
}
|
||||
}
|
||||
|
||||
// A cultural way of doing something:
|
||||
open class Custom1(i: Int) {
|
||||
init {
|
||||
trace("Custom $i")
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceSetting1(i: Int) : Custom1(i) {
|
||||
val spoon = Spoon1(i)
|
||||
val fork = Fork1(i)
|
||||
val knife = Knife1(i)
|
||||
val plate = DinnerPlate1(i)
|
||||
init {
|
||||
trace("PlaceSetting $i")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
PlaceSetting1(9)
|
||||
trace eq """
|
||||
Custom 9
|
||||
Utensil 9
|
||||
Spoon 9
|
||||
Utensil 9
|
||||
Fork 9
|
||||
Utensil 9
|
||||
Knife 9
|
||||
Plate 9
|
||||
DinnerPlate 9
|
||||
PlaceSetting 9
|
||||
"""
|
||||
}
|
||||
```
|
||||
each `init`.
|
|
@ -3,52 +3,4 @@
|
|||
Modify `ModelingMI.kt` by adding `fun resize(scale: Int): Int` to `Rectangle`,
|
||||
which just returns `scale`. Add a `fun rightClicked(): Boolean` to
|
||||
`MouseManager`. Call the new functions in `main()` and verify that `Button`
|
||||
needs no modifications to support the new interfaces.
|
||||
|
||||
```kotlin
|
||||
// ClassDelegation/ClassDelegEx1.kt
|
||||
package classdelegationex1
|
||||
import atomictest.eq
|
||||
|
||||
interface Rectangle {
|
||||
fun paint(): Int
|
||||
fun resize(scale: Int): Int
|
||||
}
|
||||
|
||||
class ButtonImage(
|
||||
val width: Int,
|
||||
val height: Int
|
||||
) : Rectangle {
|
||||
override fun paint() = width * height
|
||||
override fun resize(scale: Int) = scale
|
||||
}
|
||||
|
||||
interface MouseManager {
|
||||
fun clicked(): Boolean
|
||||
fun hovering(): Boolean
|
||||
fun rightClicked(): Boolean
|
||||
}
|
||||
|
||||
class UserInput : MouseManager {
|
||||
override fun clicked() = true
|
||||
override fun hovering() = true
|
||||
override fun rightClicked() = true
|
||||
}
|
||||
|
||||
class Button(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val image: Rectangle =
|
||||
ButtonImage(width, height),
|
||||
val input: MouseManager = UserInput()
|
||||
) : Rectangle by image, MouseManager by input
|
||||
|
||||
fun main() {
|
||||
val button = Button(10, 5)
|
||||
button.paint() eq 50
|
||||
button.clicked() eq true
|
||||
button.hovering() eq true
|
||||
button.resize(10) eq 10
|
||||
button.rightClicked() eq true
|
||||
}
|
||||
```
|
||||
needs no modifications to support the new interfaces.
|
|
@ -20,54 +20,4 @@ Interface." Modify the rest of the code to use `MyType` whenever possible,
|
|||
rather than `MyClass`, and rename `useMyClass()` to `useMyType()`.
|
||||
|
||||
Now modify `MyClassAdaptedForLib` so that `field` is `private` and is the
|
||||
delegate for `MyType`.
|
||||
|
||||
```kotlin
|
||||
// ClassDelegation/ClassDelegEx2.kt
|
||||
package classdelegationex2
|
||||
import usefullibrary.*
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface MyType {
|
||||
fun g()
|
||||
fun h()
|
||||
}
|
||||
|
||||
class MyClass: MyType {
|
||||
override fun g() = trace("g()")
|
||||
override fun h() = trace("h()")
|
||||
}
|
||||
|
||||
fun useMyType(mt: MyType) {
|
||||
mt.g()
|
||||
mt.h()
|
||||
}
|
||||
|
||||
class MyClassAdaptedForLib(
|
||||
private val field: MyClass
|
||||
) : LibType, MyType by field {
|
||||
override fun f1() = field.h()
|
||||
override fun f2() = field.g()
|
||||
}
|
||||
|
||||
fun adapt(mc: MyClass) =
|
||||
MyClassAdaptedForLib(mc)
|
||||
|
||||
fun main() {
|
||||
val library = UsefulLibrary()
|
||||
val mc = adapt(MyClass())
|
||||
library.utility1(mc)
|
||||
library.utility2(mc)
|
||||
useMyType(mc)
|
||||
trace eq """
|
||||
h()
|
||||
g()
|
||||
g()
|
||||
h()
|
||||
g()
|
||||
h()
|
||||
"""
|
||||
}
|
||||
```
|
||||
delegate for `MyType`.
|
|
@ -23,66 +23,4 @@ Create a class `AA` that implements `A`, producing the value `1` for `x` and
|
|||
`z`, and tracing `"AA.u()"` for `u()` and `"AA.v()"` for `v()`. Create a
|
||||
similar implementation `BB` implementing `B`. Now create a class `Delegation`
|
||||
which delegates to both `A` and `B`. IntelliJ or the compiler will tell you
|
||||
what you need to do to solve the collisions.
|
||||
|
||||
```kotlin
|
||||
// ClassDelegation/ClassDelegEx3.kt
|
||||
package classdelegationex3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface A {
|
||||
val x: Int
|
||||
val z: Int
|
||||
fun u()
|
||||
fun v()
|
||||
}
|
||||
|
||||
interface B {
|
||||
val y: Int
|
||||
val z: Int
|
||||
fun v()
|
||||
fun w()
|
||||
}
|
||||
|
||||
class AA : A {
|
||||
override val x = 1
|
||||
override val z = 1
|
||||
override fun u() = trace("AA.u()")
|
||||
override fun v() = trace("AA.v()")
|
||||
}
|
||||
|
||||
class BB : B {
|
||||
override val y = 1
|
||||
override val z = 1
|
||||
override fun v() = trace("BB.v()")
|
||||
override fun w() = trace("BB.w()")
|
||||
}
|
||||
|
||||
class Delegation(val a: A, val b: B) :
|
||||
A by a, B by b {
|
||||
override val z = a.z + b.z
|
||||
override fun v() {
|
||||
trace("Delegation.v()")
|
||||
a.v()
|
||||
b.v()
|
||||
trace("Delegation.z: $z")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val d = Delegation(AA(), BB())
|
||||
d.u()
|
||||
d.v()
|
||||
d.w()
|
||||
trace eq """
|
||||
AA.u()
|
||||
Delegation.v()
|
||||
AA.v()
|
||||
BB.v()
|
||||
Delegation.z: 2
|
||||
BB.w()
|
||||
"""
|
||||
}
|
||||
```
|
||||
what you need to do to solve the collisions.
|
|
@ -24,67 +24,4 @@ containing an anonymous inner class that implements `VendorFactory`.
|
|||
|
||||
Now write a standalone function `consumer(factory: VendorFactory)` which uses
|
||||
`factory` to create a `Vendor`. Use that `Vendor` to call `pencil()` and
|
||||
`pen()`. The starter code in `main()` will validate your solution.
|
||||
|
||||
```kotlin
|
||||
// CompanionObjects/CompanionEx1.kt
|
||||
package companionobjectex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Vendor {
|
||||
fun pencil()
|
||||
fun pen()
|
||||
}
|
||||
|
||||
interface VendorFactory {
|
||||
fun create(): Vendor
|
||||
}
|
||||
|
||||
class Vendor1 private constructor() : Vendor {
|
||||
override fun pencil() {
|
||||
trace("Vendor1 pencil")
|
||||
}
|
||||
override fun pen() {
|
||||
trace("Vendor1 pen")
|
||||
}
|
||||
companion object {
|
||||
var factory = object : VendorFactory {
|
||||
override fun create() = Vendor1()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Vendor2 private constructor() : Vendor {
|
||||
override fun pencil() {
|
||||
trace("Vendor2 pencil")
|
||||
}
|
||||
override fun pen() {
|
||||
trace("Vendor2 pen")
|
||||
}
|
||||
companion object {
|
||||
var factory = object : VendorFactory {
|
||||
override fun create() = Vendor2()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun consumer(factory: VendorFactory) {
|
||||
val vendor = factory.create()
|
||||
vendor.pencil()
|
||||
vendor.pen()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
// Implementations are interchangeable:
|
||||
consumer(Vendor1.factory)
|
||||
consumer(Vendor2.factory)
|
||||
trace eq """
|
||||
Vendor1 pencil
|
||||
Vendor1 pen
|
||||
Vendor2 pencil
|
||||
Vendor2 pen
|
||||
"""
|
||||
}
|
||||
```
|
||||
`pen()`. The starter code in `main()` will validate your solution.
|
|
@ -23,79 +23,4 @@ both `private` constructors with no arguments. Each class should contain a
|
|||
constructor. Each `companion object` contains a `const val max` which is used
|
||||
within `move()`. The definitions of `playGame()` and `main()` are provided;
|
||||
complete the code in `Checkers` and `Chess` so it satisfies the tests in
|
||||
`main()`.
|
||||
|
||||
```kotlin
|
||||
// CompanionObjects/CompanionEx2.kt
|
||||
package companionobjectex2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface BoardGame {
|
||||
fun move(): Boolean
|
||||
companion object {
|
||||
fun createBoard() {
|
||||
trace("BoardGame.createBoard()")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface GameFactory {
|
||||
fun create(): BoardGame
|
||||
}
|
||||
|
||||
class
|
||||
Checkers private constructor() : BoardGame {
|
||||
private var moves = 0
|
||||
override fun move(): Boolean {
|
||||
trace("Checkers move $moves")
|
||||
return ++moves != max
|
||||
}
|
||||
companion object Factory : GameFactory {
|
||||
private const val max = 3
|
||||
init {
|
||||
BoardGame.createBoard()
|
||||
}
|
||||
override fun create() = Checkers()
|
||||
}
|
||||
}
|
||||
|
||||
class
|
||||
Chess private constructor() : BoardGame {
|
||||
private var moves = 0
|
||||
override fun move(): Boolean {
|
||||
trace("Chess move $moves")
|
||||
return ++moves != max
|
||||
}
|
||||
companion object Factory : GameFactory {
|
||||
private const val max = 4
|
||||
init {
|
||||
BoardGame.createBoard()
|
||||
}
|
||||
override fun create() = Chess()
|
||||
}
|
||||
}
|
||||
|
||||
fun playGame(factory: GameFactory) {
|
||||
val s = factory.create()
|
||||
while (s.move())
|
||||
;
|
||||
}
|
||||
|
||||
fun main() {
|
||||
playGame(Checkers.Factory)
|
||||
playGame(Chess.Factory)
|
||||
trace eq """
|
||||
BoardGame.createBoard()
|
||||
Checkers move 0
|
||||
Checkers move 1
|
||||
Checkers move 2
|
||||
BoardGame.createBoard()
|
||||
Chess move 0
|
||||
Chess move 1
|
||||
Chess move 2
|
||||
Chess move 3
|
||||
"""
|
||||
}
|
||||
```
|
||||
`main()`.
|
|
@ -55,85 +55,4 @@ steps during object creation. `applyForAccount()` begins the creation of a new
|
|||
the `Account`, and `completeAccount()` takes an `ID`, adds an account `number`,
|
||||
and moves the `Account` from the `applied` to the `accounts` list.
|
||||
|
||||
The starter code in `main()` will validate your solution.
|
||||
|
||||
```kotlin
|
||||
// CompanionObjects/CompanionEx3.kt
|
||||
package companionobjectex3
|
||||
import kotlin.random.Random
|
||||
import atomictest.*
|
||||
|
||||
data class ID(
|
||||
val name: String,
|
||||
val id: String = idGenerator()
|
||||
) {
|
||||
companion object {
|
||||
const val size = 10
|
||||
private val r = Random(47)
|
||||
private val source =
|
||||
('a'..'z') + ('A'..'Z') + ('0'..'9')
|
||||
private fun idGenerator() =
|
||||
(1..size).map { source.random(r) }
|
||||
.joinToString("")
|
||||
fun test(n: Int) = List(n) {
|
||||
ID(('a' + it).toString())
|
||||
}.joinToString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
class Bank(val name: String) {
|
||||
private val applied =
|
||||
mutableListOf<Account>()
|
||||
private val accounts =
|
||||
mutableListOf<Account>()
|
||||
private class Account(val name: String) {
|
||||
var id: ID? = null
|
||||
private var number: Long? = null
|
||||
fun addID(verifiedID: ID) {
|
||||
id = verifiedID
|
||||
}
|
||||
companion object Numbers {
|
||||
private var i: Long = 1000
|
||||
private fun nextAccountNumber() = i++
|
||||
}
|
||||
fun finish() {
|
||||
number = nextAccountNumber()
|
||||
}
|
||||
override fun toString() = "$id $number"
|
||||
}
|
||||
fun applyForAccount(name: String) =
|
||||
applied.add(Account(name))
|
||||
fun addID(id: ID) = applied
|
||||
.first { it.name == id.name }.addID(id)
|
||||
fun completeAccount(verifiedID: ID) {
|
||||
val account =
|
||||
applied.first { it.id == verifiedID }
|
||||
account.finish()
|
||||
accounts.add(account)
|
||||
applied.remove(account)
|
||||
}
|
||||
override fun toString() =
|
||||
accounts.joinToString("\n")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
ID.test(4) eq """
|
||||
ID(name=a, id=weazblk9UF),
|
||||
ID(name=b, id=LCcPJtUGh5),
|
||||
ID(name=c, id=Acs5BzSj6m),
|
||||
ID(name=d, id=gHrshDvhwc)
|
||||
""".trimIndent()
|
||||
val bank = Bank("Jerry's Savings & Loan")
|
||||
listOf(ID("Morty Smith"), ID("Beth Smith"),
|
||||
ID("Summer Smith")).forEach {
|
||||
bank.applyForAccount(it.name)
|
||||
bank.addID(it)
|
||||
bank.completeAccount(it)
|
||||
}
|
||||
bank eq """
|
||||
ID(name=Morty Smith, id=ePkc0HjTAU) 1000
|
||||
ID(name=Beth Smith, id=jjePlEO93w) 1001
|
||||
ID(name=Summer Smith, id=C8yaeFEgv6) 1002
|
||||
""".trimIndent()
|
||||
}
|
||||
```
|
||||
The starter code in `main()` will validate your solution.
|
|
@ -1,22 +1,4 @@
|
|||
## Complex Constructors (#1)
|
||||
|
||||
Modify `Alien` by making `name` `private`, then giving it a constructor that
|
||||
assigns its `name` to the `val` property `myName`.
|
||||
|
||||
```kotlin
|
||||
// ComplexConstructors/Exercise1.kt
|
||||
package complexconstructors
|
||||
import atomictest.eq
|
||||
|
||||
class Alien2(private val name: String) {
|
||||
val myName: String
|
||||
init {
|
||||
myName = name
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val alien = Alien2("Pencilvester")
|
||||
alien.myName eq "Pencilvester"
|
||||
}
|
||||
```
|
||||
assigns its `name` to the `val` property `myName`.
|
|
@ -3,23 +3,4 @@
|
|||
Create a class called `SumChars` that takes `text: String` as a class
|
||||
argument. Add an un-initialized property `val sum: Int`. Inside `init`,
|
||||
define a local function `calc()` that sums up the integer values of each
|
||||
of the characters in `text`. Call `calc()` and assign the result to `sum`.
|
||||
|
||||
```kotlin
|
||||
// ComplexConstructors/Exercise2.kt
|
||||
package complexconstructors
|
||||
import atomictest.eq
|
||||
|
||||
class SumChars(text: String) {
|
||||
val sum: Int
|
||||
init {
|
||||
fun calc() =
|
||||
text.fold(0, {s, c -> s + c.toInt() })
|
||||
sum = calc()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
SumChars("What?").sum eq 467
|
||||
}
|
||||
```
|
||||
of the characters in `text`. Call `calc()` and assign the result to `sum`.
|
|
@ -3,27 +3,4 @@
|
|||
Show that multiple init sections are executed in declaration order. Create a
|
||||
class containing an uninitialized `var result: String`, then initialize
|
||||
`result` to `one two three` in three different `init` blocks. Note that
|
||||
`result` cannot be a `val` in this case.
|
||||
|
||||
```kotlin
|
||||
// ComplexConstructors/Exercise3.kt
|
||||
package complexconstructors
|
||||
import atomictest.eq
|
||||
|
||||
class MultipleInit() {
|
||||
var result: String
|
||||
init {
|
||||
result = "one "
|
||||
}
|
||||
init {
|
||||
result += "two "
|
||||
}
|
||||
init {
|
||||
result += "three"
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
MultipleInit().result eq "one two three"
|
||||
}
|
||||
```
|
||||
`result` cannot be a `val` in this case.
|
|
@ -1,60 +1,4 @@
|
|||
## Composition (#1)
|
||||
|
||||
In `Car.kt`, add a `service()` member function to `Engine` and add a call to
|
||||
this function in `main()`.
|
||||
|
||||
```kotlin
|
||||
// Composition/CompositionExercise1.kt
|
||||
package compositionex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
class Engine {
|
||||
fun start() = trace("Engine start")
|
||||
fun stop() = trace("Engine stop")
|
||||
fun service() = trace("Engine service")
|
||||
}
|
||||
|
||||
class Wheel {
|
||||
fun inflate(psi: Int) =
|
||||
trace("Wheel inflate($psi)")
|
||||
}
|
||||
|
||||
class Window(val side: String) {
|
||||
fun rollUp() =
|
||||
trace("$side Window roll up")
|
||||
fun rollDown() =
|
||||
trace("$side Window roll down")
|
||||
}
|
||||
|
||||
class Door(val side: String) {
|
||||
val window = Window(side)
|
||||
fun open() = trace("$side Door open")
|
||||
fun close() = trace("$side Door close")
|
||||
}
|
||||
|
||||
class Car {
|
||||
val engine = Engine()
|
||||
val wheel = List(4) { Wheel() }
|
||||
// Two door:
|
||||
val leftDoor = Door("left")
|
||||
val rightDoor = Door("right")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val car = Car()
|
||||
car.leftDoor.open()
|
||||
car.rightDoor.window.rollUp()
|
||||
car.wheel[0].inflate(72)
|
||||
car.engine.start()
|
||||
car.engine.service()
|
||||
trace eq """
|
||||
left Door open
|
||||
right Window roll up
|
||||
Wheel inflate(72)
|
||||
Engine start
|
||||
Engine service
|
||||
"""
|
||||
}
|
||||
```
|
||||
this function in `main()`.
|
|
@ -7,64 +7,4 @@ classes, and a `List<Bathroom>` and `List<Bedroom>` to `House`. Create a
|
|||
`Bathroom`s called "Master", "Shared", and "Guest". Add `Bedroom`s called
|
||||
"Master", "Son", "Daughter", and "Guest". Use `atomictest.Trace` to capture all
|
||||
the class names and `name` properties as the `House` is constructed. Also
|
||||
notice the order of construction.
|
||||
|
||||
```kotlin
|
||||
// Composition/CompositionExercise2.kt
|
||||
package compositionex2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
open class Building(val name: String) {
|
||||
init { trace("Building: $name") }
|
||||
}
|
||||
|
||||
class Kitchen(val name: String) {
|
||||
init { trace("Kitchen: $name") }
|
||||
}
|
||||
|
||||
class Bedroom(val name: String) {
|
||||
init { trace("Bedroom: $name") }
|
||||
}
|
||||
|
||||
class Bathroom(val name: String) {
|
||||
init { trace("Bathroom: $name") }
|
||||
}
|
||||
|
||||
class House(name: String) : Building(name) {
|
||||
val kitchens = listOf(
|
||||
Kitchen("Main"),
|
||||
Kitchen("Guest")
|
||||
)
|
||||
val bathrooms = listOf(
|
||||
Bathroom("Master"),
|
||||
Bathroom("Shared"),
|
||||
Bathroom("Guest")
|
||||
)
|
||||
val bedrooms = listOf(
|
||||
Bedroom("Master"),
|
||||
Bedroom("Son"),
|
||||
Bedroom("Daughter"),
|
||||
Bedroom("Guest")
|
||||
)
|
||||
init { trace("House: $name") }
|
||||
}
|
||||
|
||||
fun main() {
|
||||
House("Our House")
|
||||
trace eq """
|
||||
Building: Our House
|
||||
Kitchen: Main
|
||||
Kitchen: Guest
|
||||
Bathroom: Master
|
||||
Bathroom: Shared
|
||||
Bathroom: Guest
|
||||
Bedroom: Master
|
||||
Bedroom: Son
|
||||
Bedroom: Daughter
|
||||
Bedroom: Guest
|
||||
House: Our House
|
||||
"""
|
||||
}
|
||||
```
|
||||
notice the order of construction.
|
|
@ -18,85 +18,4 @@ you write:
|
|||
- `InMemory`, implemented with a `mutableMapOf<String, String>()`
|
||||
- `Mock`, implemented with two `String` `var`s.
|
||||
|
||||
Write these classes so they pass the tests given in `main()`.
|
||||
|
||||
```kotlin
|
||||
// Composition/CompositionExercise3.kt
|
||||
package compositionex3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface DataBase {
|
||||
fun write(key: String, value: String)
|
||||
fun read(key: String): String
|
||||
}
|
||||
|
||||
class NonRelational : DataBase {
|
||||
private val db =
|
||||
mutableListOf<Pair<String, String>>()
|
||||
override fun write(
|
||||
key: String, value: String
|
||||
) {
|
||||
db.add(Pair(key, value))
|
||||
}
|
||||
override fun read(key: String) =
|
||||
db.first { it.first == key }.second
|
||||
}
|
||||
|
||||
class InMemory : DataBase {
|
||||
private val db =
|
||||
mutableMapOf<String, String>()
|
||||
override fun write(
|
||||
key: String, value: String
|
||||
) {
|
||||
db[key] = value
|
||||
}
|
||||
override fun read(key: String) =
|
||||
db[key] ?: ""
|
||||
}
|
||||
|
||||
class Mock : DataBase {
|
||||
private var k = ""
|
||||
private var v = ""
|
||||
override fun write(
|
||||
key: String, value: String
|
||||
) { k = key; v = value }
|
||||
override fun read(key: String) = v
|
||||
}
|
||||
|
||||
class Holder(val db: DataBase) {
|
||||
fun store(k: String, v: String) =
|
||||
db.write(k, v)
|
||||
fun fetch(k: String) = db.read(k)
|
||||
private val data = """
|
||||
color: purple
|
||||
dog: husky
|
||||
art: deco
|
||||
""".trimIndent().lines()
|
||||
.map { it.split(": ") }
|
||||
fun test() {
|
||||
for(line in data) {
|
||||
store(line[0], line[1])
|
||||
trace(fetch(line[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Holder(NonRelational()).test()
|
||||
Holder(InMemory()).test()
|
||||
Holder(Mock()).test()
|
||||
trace eq """
|
||||
purple
|
||||
husky
|
||||
deco
|
||||
purple
|
||||
husky
|
||||
deco
|
||||
purple
|
||||
husky
|
||||
deco
|
||||
"""
|
||||
}
|
||||
```
|
||||
Write these classes so they pass the tests given in `main()`.
|
|
@ -7,46 +7,4 @@ function. The `trace` argument is the class name and function; for example
|
|||
|
||||
Now write a function `checkAndCall(b: Base)` so that it calls the polymorphic
|
||||
member function, then uses a `when` to downcast and call the extended-interface
|
||||
functions.
|
||||
|
||||
```kotlin
|
||||
// DownCasting/DownCastEx1.kt
|
||||
package downcastingex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Base {
|
||||
fun f()
|
||||
}
|
||||
|
||||
class Derived1 : Base {
|
||||
override fun f() = trace("Derived1.f()")
|
||||
fun g() = trace("Derived1.g()")
|
||||
}
|
||||
|
||||
class Derived2 : Base {
|
||||
override fun f() = trace("Derived2.f()")
|
||||
fun h() = trace("Derived2.h()")
|
||||
}
|
||||
|
||||
fun checkAndCall(b: Base) {
|
||||
b.f() // Polymorphic call
|
||||
when(b) {
|
||||
is Derived1 -> b.g()
|
||||
is Derived2 -> b.h()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
checkAndCall(Derived1()) // Upcast
|
||||
checkAndCall(Derived2()) // Upcast
|
||||
trace eq """
|
||||
Derived1.f()
|
||||
Derived1.g()
|
||||
Derived2.f()
|
||||
Derived2.h()
|
||||
"""
|
||||
}
|
||||
|
||||
```
|
||||
functions.
|
|
@ -9,17 +9,4 @@ There's a second, more dynamic form of `filterIsInstance()`, which takes a
|
|||
class object as a parameter. Pass `Dog::class.java` as the argument to
|
||||
`filterIsInstance()`, with the rest of the expression as described in the
|
||||
previous paragraph. How is this version of `filterIsInstance()` different from
|
||||
the version that uses a generic argument?
|
||||
|
||||
```kotlin
|
||||
// DownCasting/DownCastEx2.kt
|
||||
package downcasting
|
||||
import atomictest.eq
|
||||
|
||||
fun main() {
|
||||
group.filterIsInstance<Dog>()
|
||||
.map(Dog::bark) eq "[Yip!, Yip!]"
|
||||
group.filterIsInstance(Dog::class.java)
|
||||
.map(Dog::bark) eq "[Yip!, Yip!]"
|
||||
}
|
||||
```
|
||||
the version that uses a generic argument?
|
|
@ -14,67 +14,4 @@ Create a duplicate hierarchy from an interface `Animal2`, but in this case make
|
|||
`Beetle2`. In `main()`, create a `List` of `Animal2` objects and call `move()`
|
||||
for each one.
|
||||
|
||||
Compare the type-checked hierarchy to the polymorphic hierarchy.
|
||||
|
||||
```kotlin
|
||||
// DownCasting/DownCastEx3.kt
|
||||
package downcastingex3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Animal1
|
||||
|
||||
class Worm1: Animal1 {
|
||||
fun wriggle() = trace("Wriggle")
|
||||
}
|
||||
|
||||
class Snail1: Animal1 {
|
||||
fun slide() = trace("Slide")
|
||||
}
|
||||
|
||||
class Beetle1: Animal1 {
|
||||
fun walk() = trace("Walk")
|
||||
}
|
||||
|
||||
fun move(a1: Animal1) {
|
||||
when(a1) {
|
||||
is Worm1 -> a1.wriggle()
|
||||
is Snail1 -> a1.slide()
|
||||
is Beetle1 -> a1.walk()
|
||||
}
|
||||
}
|
||||
|
||||
interface Animal2 {
|
||||
fun move()
|
||||
}
|
||||
|
||||
class Worm2: Animal2 {
|
||||
override fun move() = trace("Wriggle")
|
||||
}
|
||||
|
||||
class Snail2: Animal2 {
|
||||
override fun move() = trace("Slide")
|
||||
}
|
||||
|
||||
class Beetle2: Animal2 {
|
||||
override fun move() = trace("Walk")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val animals1 =
|
||||
listOf(Worm1(), Snail1(), Beetle1())
|
||||
animals1.forEach { move(it) }
|
||||
val animals2 =
|
||||
listOf(Worm2(), Snail2(), Beetle2())
|
||||
animals2.forEach { it.move() }
|
||||
trace eq """
|
||||
Wriggle
|
||||
Slide
|
||||
Walk
|
||||
Wriggle
|
||||
Slide
|
||||
Walk
|
||||
"""
|
||||
}
|
||||
```
|
||||
Compare the type-checked hierarchy to the polymorphic hierarchy.
|
|
@ -4,91 +4,4 @@ Following `AdjustTemp.kt`, add two extension functions `openWindow()` and
|
|||
`fan()` as ways of cooling. Add a class `DeltaTemperature2` where the extension
|
||||
functions are instead member functions, and an overloaded `adjust()` function
|
||||
which takes a `DeltaTemperature2`. Which approach seems better, or are they
|
||||
about the same?
|
||||
|
||||
```kotlin
|
||||
// InheritanceExtensions/InhExtensionsEx1.kt
|
||||
package inheritanceextensionsex1
|
||||
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
class DeltaTemperature(
|
||||
val current: Double,
|
||||
val target: Double
|
||||
)
|
||||
|
||||
fun DeltaTemperature.heat() {
|
||||
if (current < target)
|
||||
trace("heating to $target")
|
||||
}
|
||||
|
||||
fun DeltaTemperature.cool() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
|
||||
fun DeltaTemperature.openWindow() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
|
||||
fun DeltaTemperature.fan() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
|
||||
class DeltaTemperature2(
|
||||
val current: Double,
|
||||
val target: Double
|
||||
) {
|
||||
fun heat() {
|
||||
if (current < target)
|
||||
trace("heating to $target")
|
||||
}
|
||||
fun cool() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
fun openWindow() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
fun fan() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
}
|
||||
|
||||
fun adjust(deltaT: DeltaTemperature) {
|
||||
deltaT.heat()
|
||||
deltaT.cool()
|
||||
deltaT.openWindow()
|
||||
deltaT.fan()
|
||||
}
|
||||
|
||||
fun adjust(deltaT: DeltaTemperature2) {
|
||||
deltaT.heat()
|
||||
deltaT.cool()
|
||||
deltaT.openWindow()
|
||||
deltaT.fan()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
adjust(DeltaTemperature(60.0, 70.0))
|
||||
adjust(DeltaTemperature(80.0, 60.0))
|
||||
adjust(DeltaTemperature2(60.0, 70.0))
|
||||
adjust(DeltaTemperature2(80.0, 60.0))
|
||||
trace eq """
|
||||
heating to 70.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
heating to 70.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
"""
|
||||
}
|
||||
```
|
||||
about the same?
|
|
@ -8,74 +8,4 @@ Refactor `BatteryPet2.kt` to improve the design:
|
|||
- You no longer need `Dog.sit()`
|
||||
- Change `playWithDog(dog: Dog)` to `playWithPet(pet: Pet)`
|
||||
- Add a `CatFood` type of `Energy`, and define the associated `Cat` class
|
||||
- In `main()`, test all types of `Pet`
|
||||
|
||||
```kotlin
|
||||
// InheritanceExtensions/InhExtensionsEx2.kt
|
||||
package inheritanceextensionsex2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Energy {
|
||||
fun replenish() = trace("Fill Bowl")
|
||||
}
|
||||
|
||||
open class Pet(open val energy: Energy) {
|
||||
open fun speak() = trace("")
|
||||
open fun settle() = trace("")
|
||||
open fun feed() = energy.replenish()
|
||||
}
|
||||
|
||||
class DogFood : Energy
|
||||
|
||||
open class Dog : Pet(DogFood()) {
|
||||
override fun speak() = trace("Bark!")
|
||||
override fun settle() = trace("Sitting...")
|
||||
}
|
||||
|
||||
class CatFood : Energy
|
||||
|
||||
open class Cat : Pet(CatFood()) {
|
||||
override fun speak() = trace("Meow!")
|
||||
override fun settle() =
|
||||
trace("In my basket...")
|
||||
}
|
||||
|
||||
class Batteries : Energy {
|
||||
override fun replenish() =
|
||||
trace("Change batteries")
|
||||
}
|
||||
|
||||
class ToyDog: Dog() {
|
||||
override val energy = Batteries()
|
||||
}
|
||||
|
||||
fun play(pet: Pet) = pet.speak()
|
||||
|
||||
fun playWithPet(pet: Pet) {
|
||||
play(pet)
|
||||
pet.settle()
|
||||
pet.energy.replenish()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val dog1 = Dog()
|
||||
val dog2 = ToyDog()
|
||||
val cat = Cat()
|
||||
playWithPet(dog1)
|
||||
playWithPet(dog2)
|
||||
playWithPet(cat)
|
||||
trace eq """
|
||||
Bark!
|
||||
Sitting...
|
||||
Fill Bowl
|
||||
Bark!
|
||||
Sitting...
|
||||
Change batteries
|
||||
Meow!
|
||||
In my basket...
|
||||
Fill Bowl
|
||||
"""
|
||||
}
|
||||
```
|
||||
- In `main()`, test all types of `Pet`
|
|
@ -4,49 +4,4 @@ Starting with `Adapter.kt` and `ComposeAdapter.kt`, create a function which
|
|||
dynamically adapts an object, accepting a `MyClass` and returning a
|
||||
`MyClassAdaptedForLib`. Is it possible to use the inheritance approach in
|
||||
`Adapter.kt`, or are you forced to use composition? (We'll revisit this issue
|
||||
in [Class Delegation]).
|
||||
|
||||
```kotlin
|
||||
// InheritanceExtensions/InhExtensionsEx3.kt
|
||||
package inheritanceextensionsex3
|
||||
import usefullibrary.*
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
class MyClass {
|
||||
fun g() = trace("g()")
|
||||
fun h() = trace("h()")
|
||||
}
|
||||
|
||||
fun useMyClass(mc: MyClass) {
|
||||
mc.g()
|
||||
mc.h()
|
||||
}
|
||||
|
||||
class MyClassAdaptedForLib(
|
||||
val field: MyClass
|
||||
) : LibType {
|
||||
override fun f1() = field.h()
|
||||
override fun f2() = field.g()
|
||||
}
|
||||
|
||||
fun adapt(mc: MyClass) =
|
||||
MyClassAdaptedForLib(mc)
|
||||
|
||||
fun main() {
|
||||
val library = UsefulLibrary()
|
||||
val mc = adapt(MyClass())
|
||||
library.utility1(mc)
|
||||
library.utility2(mc)
|
||||
useMyClass(mc.field)
|
||||
trace eq """
|
||||
h()
|
||||
g()
|
||||
g()
|
||||
h()
|
||||
g()
|
||||
h()
|
||||
"""
|
||||
}
|
||||
```
|
||||
in [Class Delegation]).
|
|
@ -2,44 +2,4 @@
|
|||
|
||||
Add a `hamster()` member function to `LocalInnerClasses.kt` with a local
|
||||
`val poke = "Squeak"` which returns an object of an anonymous inner class that
|
||||
extends `Pet`, with a `speak()` that produces `poke + home()`.
|
||||
|
||||
```kotlin
|
||||
// InnerClasses/InnerEx1.kt
|
||||
package innerclassesex1
|
||||
import atomictest.eq
|
||||
|
||||
interface Pet {
|
||||
fun speak(): String
|
||||
}
|
||||
|
||||
class PetCreator {
|
||||
fun home() = " home!"
|
||||
fun dog(): Pet {
|
||||
val say = "Bark"
|
||||
class Dog : Pet {
|
||||
override fun speak() = say + home()
|
||||
}
|
||||
return Dog()
|
||||
}
|
||||
fun cat(): Pet {
|
||||
val emit = "Meow"
|
||||
return object : Pet {
|
||||
override fun speak() = emit + home()
|
||||
}
|
||||
}
|
||||
fun hamster(): Pet {
|
||||
val poke = "Squeak"
|
||||
return object : Pet {
|
||||
override fun speak() = poke + home()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val create = PetCreator()
|
||||
create.dog().speak() eq "Bark home!"
|
||||
create.cat().speak() eq "Meow home!"
|
||||
create.hamster().speak() eq "Squeak home!"
|
||||
}
|
||||
```
|
||||
extends `Pet`, with a `speak()` that produces `poke + home()`.
|
|
@ -15,28 +15,4 @@ function `f()` which returns `"Any-based"`.
|
|||
|
||||
Add a member function `manifest()` which produces a `String` containing the
|
||||
properties `contains` and `label` and the output of `f()`. The starter code in
|
||||
`main()` will test your solution.
|
||||
|
||||
```kotlin
|
||||
// InnerClasses/InnerEx2.kt
|
||||
package innerclassesex2
|
||||
import atomictest.eq
|
||||
|
||||
class Box(contains: String, label: String) {
|
||||
inner class Contents(val contains: String)
|
||||
inner class Destination(val label: String)
|
||||
private val contents = Contents(contains)
|
||||
private val destination = Destination(label)
|
||||
private val x = object : Any() {
|
||||
fun f() = "Any-based"
|
||||
}
|
||||
fun manifest() =
|
||||
"${contents.contains}: " +
|
||||
"${destination.label} ${x.f()}"
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Box("Computer", "Tasmania").manifest() eq
|
||||
"Computer: Tasmania Any-based"
|
||||
}
|
||||
```
|
||||
`main()` will test your solution.
|
|
@ -27,66 +27,4 @@ Now make `Container` inherit from `Iterable<T>`, and add a function called
|
|||
from `Iterator<T>`. Add a standalone function `<T> traceAll2(ib: Iterable<T>)`
|
||||
that produces the same behavior as `traceAll()`.
|
||||
|
||||
The starter code contains a `main()` with tests to verify your code.
|
||||
|
||||
```kotlin
|
||||
// InnerClasses/InnerEx3.kt
|
||||
package innerclassesex3
|
||||
import atomictest.*
|
||||
|
||||
interface Selector<T> {
|
||||
fun end(): Boolean
|
||||
fun current(): T
|
||||
fun next()
|
||||
}
|
||||
|
||||
class Container<T>(
|
||||
iterable: Iterable<T>
|
||||
) : Iterable<T> {
|
||||
private val items = iterable.toMutableList()
|
||||
fun add(x: T) = items.add(x)
|
||||
fun selector() = object : Selector<T> {
|
||||
private var i = 0
|
||||
override fun end() = i == items.size
|
||||
override fun current() = items[i]
|
||||
override fun next() {
|
||||
if (i < items.size) i++
|
||||
}
|
||||
}
|
||||
override
|
||||
fun iterator() = object : Iterator<T> {
|
||||
private var i = 0
|
||||
override fun hasNext() = i < items.size
|
||||
override fun next(): T = items[i++]
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> traceAll(select: Selector<T>): Trace {
|
||||
val trace = Trace()
|
||||
while (!select.end()) {
|
||||
trace("${select.current()}")
|
||||
select.next()
|
||||
}
|
||||
return trace
|
||||
}
|
||||
|
||||
fun <T> traceAll2(ib: Iterable<T>): Trace {
|
||||
val trace = Trace()
|
||||
val iter = ib.iterator()
|
||||
while (iter.hasNext())
|
||||
trace("${iter.next()}")
|
||||
return trace
|
||||
}
|
||||
|
||||
fun main() {
|
||||
traceAll(Container(0..9).selector()) eq
|
||||
"0 1 2 3 4 5 6 7 8 9"
|
||||
traceAll2(Container(0..9)) eq
|
||||
"0 1 2 3 4 5 6 7 8 9"
|
||||
traceAll(Container(List(10){ it * it })
|
||||
.selector()) eq
|
||||
"0 1 4 9 16 25 36 49 64 81"
|
||||
traceAll2(Container(List(10){ it * it })) eq
|
||||
"0 1 4 9 16 25 36 49 64 81"
|
||||
}
|
||||
```
|
||||
The starter code contains a `main()` with tests to verify your code.
|
|
@ -5,61 +5,4 @@ In `FillIt.kt`, `State` is only used to determine when there are no more
|
|||
returning a `Boolean` from `turn()`. Once you have that working, add a third
|
||||
player by changing `enum class Mark { BLANK, X ,O }` to `enum class Mark {
|
||||
BLANK, X, Y, Z }`. In `turn()`, use a `when` expression to move to the next
|
||||
`player` value.
|
||||
|
||||
```kotlin
|
||||
// NestedClasses/NestedEx1.kt
|
||||
package nestedclassesex1
|
||||
import atomictest.*
|
||||
import nestedclassesex1.Game.Mark.*
|
||||
import java.lang.IllegalStateException
|
||||
import kotlin.random.Random
|
||||
|
||||
interface Game {
|
||||
enum class Mark { BLANK, X, Y, Z }
|
||||
}
|
||||
|
||||
class FillIt(
|
||||
val side: Int = 3, randomSeed: Int = 0
|
||||
) : Game {
|
||||
val rand = Random(randomSeed)
|
||||
private var grid =
|
||||
MutableList(side * side) { BLANK }
|
||||
private var player = X
|
||||
fun turn(): Boolean {
|
||||
val blanks = grid.withIndex()
|
||||
.filter { it.value == BLANK }
|
||||
if (blanks.isEmpty())
|
||||
return false
|
||||
grid[blanks.random(rand).index] = player
|
||||
player = when(player) {
|
||||
BLANK -> throw IllegalStateException()
|
||||
X -> Y
|
||||
Y -> Z
|
||||
Z -> X
|
||||
}
|
||||
return true
|
||||
}
|
||||
fun play() {
|
||||
while(turn())
|
||||
;
|
||||
}
|
||||
override fun toString() =
|
||||
grid.chunked(side).joinToString("\n")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val game = FillIt(8, 17)
|
||||
game.play()
|
||||
game eq """
|
||||
[X, Z, Y, Z, Z, Y, X, Y]
|
||||
[Y, Z, Z, Y, Z, Y, Y, Z]
|
||||
[Z, Y, Z, Y, Y, X, X, Y]
|
||||
[X, X, X, Y, Y, X, X, Y]
|
||||
[X, X, Z, X, Z, X, X, X]
|
||||
[Z, X, Z, X, Y, Z, Z, Z]
|
||||
[Y, Y, X, Y, Z, Z, Z, X]
|
||||
[Y, X, Z, Y, Y, X, X, Z]
|
||||
""".trimIndent()
|
||||
}
|
||||
```
|
||||
`player` value.
|
|
@ -1,67 +1,4 @@
|
|||
## Nested Classes (#2)
|
||||
|
||||
In `NestedHouse.kt`, add two `Drawer`s to `Closet` and a `Bathtub` to
|
||||
`Bathroom`.
|
||||
|
||||
```kotlin
|
||||
// NestedClasses/NestedEx2.kt
|
||||
package nestedclassesex2
|
||||
import atomictest.*
|
||||
|
||||
abstract class Cleanable(val id: String) {
|
||||
open val parts: List<Cleanable> = listOf()
|
||||
fun clean(): String =
|
||||
"${parts.map(Cleanable::clean)} $id clean\n"
|
||||
}
|
||||
|
||||
class House: Cleanable("House") {
|
||||
override val parts = listOf(
|
||||
Bedroom("Master Bedroom"),
|
||||
Bedroom("Guest Bedroom")
|
||||
)
|
||||
class Bedroom(id: String): Cleanable(id) {
|
||||
override val parts =
|
||||
listOf(Closet(), Bathroom())
|
||||
class Closet: Cleanable("Closet") {
|
||||
override val parts = listOf(
|
||||
Shelf(), Shelf(), Drawer(), Drawer()
|
||||
)
|
||||
class Shelf: Cleanable("Shelf")
|
||||
class Drawer: Cleanable("Drawer")
|
||||
}
|
||||
class Bathroom: Cleanable("Bathroom") {
|
||||
override val parts =
|
||||
listOf(Toilet(), Sink(), Bathtub())
|
||||
class Toilet: Cleanable("Toilet")
|
||||
class Sink: Cleanable("Sink")
|
||||
class Bathtub: Cleanable("Bathtub")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
House().clean().trim() eq """
|
||||
[[[[] Shelf clean
|
||||
, [] Shelf clean
|
||||
, [] Drawer clean
|
||||
, [] Drawer clean
|
||||
] Closet clean
|
||||
, [[] Toilet clean
|
||||
, [] Sink clean
|
||||
, [] Bathtub clean
|
||||
] Bathroom clean
|
||||
] Master Bedroom clean
|
||||
, [[[] Shelf clean
|
||||
, [] Shelf clean
|
||||
, [] Drawer clean
|
||||
, [] Drawer clean
|
||||
] Closet clean
|
||||
, [[] Toilet clean
|
||||
, [] Sink clean
|
||||
, [] Bathtub clean
|
||||
] Bathroom clean
|
||||
] Guest Bedroom clean
|
||||
] House clean
|
||||
""".trimIndent()
|
||||
}
|
||||
```
|
||||
`Bathroom`.
|
|
@ -1,64 +1,4 @@
|
|||
## Nested Classes (#3)
|
||||
|
||||
"Unpack" the nested classes in `NestedHouse.kt` by turning each nested class
|
||||
into a global class.
|
||||
|
||||
```
|
||||
// NestedClasses/NestedEx3.kt
|
||||
package nestedclassesex3
|
||||
import atomictest.*
|
||||
|
||||
abstract class Cleanable(val id: String) {
|
||||
open val parts: List<Cleanable> = listOf()
|
||||
fun clean(): String =
|
||||
"${parts.map(Cleanable::clean)} $id clean\n"
|
||||
}
|
||||
|
||||
class Shelf : Cleanable("Shelf")
|
||||
|
||||
class Closet : Cleanable("Closet") {
|
||||
override val parts =
|
||||
listOf(Shelf(), Shelf())
|
||||
}
|
||||
|
||||
class Toilet : Cleanable("Toilet")
|
||||
|
||||
class Sink : Cleanable("Sink")
|
||||
|
||||
class Bathroom : Cleanable("Bathroom") {
|
||||
override val parts =
|
||||
listOf(Toilet(), Sink())
|
||||
}
|
||||
|
||||
class Bedroom(id: String) : Cleanable(id) {
|
||||
override val parts =
|
||||
listOf(Closet(), Bathroom())
|
||||
}
|
||||
|
||||
class House : Cleanable("House") {
|
||||
override val parts = listOf(
|
||||
Bedroom("Master Bedroom"),
|
||||
Bedroom("Guest Bedroom")
|
||||
)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Trace(House().clean()) eq """
|
||||
[[[[] Shelf clean
|
||||
, [] Shelf clean
|
||||
] Closet clean
|
||||
, [[] Toilet clean
|
||||
, [] Sink clean
|
||||
] Bathroom clean
|
||||
] Master Bedroom clean
|
||||
, [[[] Shelf clean
|
||||
, [] Shelf clean
|
||||
] Closet clean
|
||||
, [[] Toilet clean
|
||||
, [] Sink clean
|
||||
] Bathroom clean
|
||||
] Guest Bedroom clean
|
||||
] House clean
|
||||
"""
|
||||
}
|
||||
```
|
||||
into a global class.
|
|
@ -10,64 +10,4 @@ for each `Seat` type. `Seat` contains `abstract` member functions for
|
|||
`upgrade()` and `meal()` functions which forward their actions to the `Seat`
|
||||
object.
|
||||
|
||||
Bonus (Challenging): Why can't you use delegation for `seat` in `Ticket`?
|
||||
|
||||
```kotlin
|
||||
// NestedClasses/NestedEx4.kt
|
||||
package nestedclassesex4
|
||||
import atomictest.eq
|
||||
import nestedclassesex4.Seat.*
|
||||
|
||||
abstract class Seat {
|
||||
abstract fun upgrade(): Seat
|
||||
abstract fun meal(): String
|
||||
override fun toString() =
|
||||
"${this::class.simpleName}"
|
||||
class Coach : Seat() {
|
||||
override fun upgrade() = Premium()
|
||||
override fun meal() = "Bag Meal"
|
||||
}
|
||||
class Premium : Seat() {
|
||||
override fun upgrade() = Business()
|
||||
override fun meal() = "Bag Meal + Cookie"
|
||||
}
|
||||
class Business : Seat() {
|
||||
override fun upgrade() = First()
|
||||
override fun meal() = "Hot Meal"
|
||||
}
|
||||
class First : Seat() {
|
||||
override fun upgrade() = First()
|
||||
override fun meal() = "Private Chef"
|
||||
}
|
||||
}
|
||||
|
||||
class Ticket(
|
||||
private var seat: Seat = Coach()
|
||||
) {
|
||||
fun upgrade(): Seat {
|
||||
seat = seat.upgrade()
|
||||
return seat
|
||||
}
|
||||
fun meal() = seat.meal()
|
||||
override fun toString() = "$seat"
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val tickets = listOf(
|
||||
Ticket(),
|
||||
Ticket(Premium()),
|
||||
Ticket(Business()),
|
||||
Ticket(First())
|
||||
)
|
||||
tickets.map(Ticket::meal) eq
|
||||
"[Bag Meal, Bag Meal + Cookie, " +
|
||||
"Hot Meal, Private Chef]"
|
||||
tickets.map(Ticket::upgrade) eq
|
||||
"[Premium, Business, First, First]"
|
||||
tickets eq
|
||||
"[Premium, Business, First, First]"
|
||||
tickets.map(Ticket::meal) eq
|
||||
"[Bag Meal + Cookie, Hot Meal, " +
|
||||
"Private Chef, Private Chef]"
|
||||
}
|
||||
```
|
||||
Bonus (Challenging): Why can't you use delegation for `seat` in `Ticket`?
|
|
@ -3,22 +3,4 @@
|
|||
Add new type of `Actor` called `Bomb`, with `*` as its `symbol`. When it
|
||||
encounters a `Bomb`, the `Robot` loses all its energy and the `Bomb` disappears
|
||||
from the room (so the `Robot` still enters the room where the `Bomb` was). Add
|
||||
one or more `*` to `stringMaze` to test your solution.
|
||||
|
||||
SOLUTION::
|
||||
|
||||
Add the following to `Actors.kt`:
|
||||
|
||||
```kotlin
|
||||
class Bomb(
|
||||
override val room: Room = Room()
|
||||
) : Actor() {
|
||||
override val symbol = '*'
|
||||
override fun makeActor(r: Room) = Bomb(r)
|
||||
override fun interact(robot: Robot): Room {
|
||||
robot.energy = 0 // Bomb erases energy
|
||||
room.actor = Empty(room)
|
||||
return room
|
||||
}
|
||||
}
|
||||
```
|
||||
one or more `*` to `stringMaze` to test your solution.
|
|
@ -8,96 +8,4 @@ Calculate the allocated number of moves by counting all the `Food` items in the
|
|||
maze, dividing by two and adding 50.
|
||||
|
||||
The goal of the player is to reach the end of the maze before running out of
|
||||
moves while maximizing the `energy` value.
|
||||
|
||||
SOLUTION::
|
||||
|
||||
```kotlin
|
||||
// ObjectOrientedDesign/OODesignEx2.kt
|
||||
package oodesign
|
||||
import org.hexworks.zircon.api.*
|
||||
import org.hexworks.zircon.api.application.*
|
||||
import org.hexworks.zircon.api.data.*
|
||||
import org.hexworks.zircon.api.uievent.*
|
||||
import org.hexworks.zircon.api.graphics.*
|
||||
import org.hexworks.zircon.api.color.*
|
||||
|
||||
fun robotExplorer2(stage: Stage) {
|
||||
var moves = stage.maze
|
||||
.filter { it == '.' }.length / 2 + 50
|
||||
val style = StyleSet.defaultStyle()
|
||||
// Turn a character into a Tile:
|
||||
fun charTile(c: Char, s: StyleSet = style) =
|
||||
Tile.createCharacterTile(c, s)
|
||||
// Initialize the zircon terminal emulator:
|
||||
val grid = SwingApplications
|
||||
.startTileGrid(AppConfig.newBuilder()
|
||||
.withDefaultTileset(
|
||||
TrueTypeFontResources.ubuntuMono(25))
|
||||
.withSize(Size.create(
|
||||
stage.width, stage.height + 1))
|
||||
.build())
|
||||
// Strip newlines and create an iterator:
|
||||
val maze = stage.maze
|
||||
.filter { it != '\n' }.iterator()
|
||||
// Fill the grid with the maze:
|
||||
grid.size.fetchPositions().forEach {
|
||||
if (maze.hasNext())
|
||||
grid.putTile(charTile(maze.next()))
|
||||
}
|
||||
// The robot's location as a Position:
|
||||
fun robotPosition() = Position.create(
|
||||
stage.robot.room.col,
|
||||
stage.robot.room.row)
|
||||
// Create the red robot icon layer:
|
||||
val robotIcon = Layer.newBuilder()
|
||||
.withSize(Size.one())
|
||||
.withOffset(robotPosition())
|
||||
.build().apply {
|
||||
fill(charTile(stage.robot.symbol,
|
||||
style.withForegroundColor(
|
||||
ANSITileColor.RED)))
|
||||
}
|
||||
// As a layer, it can be moved around:
|
||||
grid.addLayer(robotIcon)
|
||||
// Update the character beneath the robot:
|
||||
fun updateSymbolAtRobot() {
|
||||
grid.cursorPosition = robotPosition()
|
||||
grid.putTile(
|
||||
charTile(stage.robot.room.actor.id()))
|
||||
}
|
||||
// Put a string on the blank bottom line:
|
||||
fun console(s: String) {
|
||||
grid.cursorPosition =
|
||||
Position.create(1, stage.height + 1)
|
||||
s.forEach { grid.putTile(charTile(it)) }
|
||||
}
|
||||
// Move the robot and update the screen:
|
||||
fun robotGo(urge: Urge) {
|
||||
moves--
|
||||
updateSymbolAtRobot()
|
||||
stage.robot.move(urge)
|
||||
robotIcon.moveTo(robotPosition())
|
||||
console(
|
||||
"Energy: ${stage.robot.energy} " +
|
||||
"Moves Remaining: $moves "
|
||||
)
|
||||
}
|
||||
// Respond to the keyboard arrow keys:
|
||||
grid.processKeyboardEvents(
|
||||
KeyboardEventType.KEY_PRESSED
|
||||
) { event, _ ->
|
||||
when (event.code) {
|
||||
KeyCode.UP -> robotGo(Urge.North)
|
||||
KeyCode.DOWN -> robotGo(Urge.South)
|
||||
KeyCode.LEFT -> robotGo(Urge.West)
|
||||
KeyCode.RIGHT -> robotGo(Urge.East)
|
||||
else -> Pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
robotExplorer2(Stage(stringMaze))
|
||||
}
|
||||
```
|
||||
moves while maximizing the `energy` value.
|
|
@ -24,76 +24,4 @@ class House(
|
|||
}
|
||||
```
|
||||
|
||||
Make your code satisfy the tests in `main()`.
|
||||
|
||||
SOLUTION::
|
||||
|
||||
```kotlin
|
||||
// ObjectOrientedDesign/OODesignEx3.kt
|
||||
package oodesignex3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface TalkingAnimal {
|
||||
fun create(): TalkingAnimal
|
||||
fun speak() =
|
||||
trace("I'm a ${this::class.simpleName}")
|
||||
}
|
||||
|
||||
class Monkey : TalkingAnimal {
|
||||
override fun create() = Monkey()
|
||||
}
|
||||
|
||||
class Giraffe : TalkingAnimal {
|
||||
override fun create() = Giraffe()
|
||||
override fun speak() {
|
||||
super.speak()
|
||||
trace("The view is great up here!")
|
||||
}
|
||||
}
|
||||
|
||||
class Hyena : TalkingAnimal {
|
||||
override fun create() = Hyena()
|
||||
override fun speak() {
|
||||
super.speak()
|
||||
trace("Yip Yip Yip!")
|
||||
}
|
||||
}
|
||||
|
||||
class House(
|
||||
val prototype: TalkingAnimal,
|
||||
val size: Int
|
||||
) {
|
||||
val name: String =
|
||||
"${prototype::class.simpleName} House"
|
||||
val denizens: List<TalkingAnimal> =
|
||||
List<TalkingAnimal>(size) {
|
||||
prototype.create()
|
||||
}
|
||||
fun report() {
|
||||
trace(name)
|
||||
denizens.forEach { it.speak() }
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
House(Giraffe(), 1).report()
|
||||
House(Hyena(), 2).report()
|
||||
House(Monkey(), 3).report()
|
||||
trace eq """
|
||||
Giraffe House
|
||||
I'm a Giraffe
|
||||
The view is great up here!
|
||||
Hyena House
|
||||
I'm a Hyena
|
||||
Yip Yip Yip!
|
||||
I'm a Hyena
|
||||
Yip Yip Yip!
|
||||
Monkey House
|
||||
I'm a Monkey
|
||||
I'm a Monkey
|
||||
I'm a Monkey
|
||||
"""
|
||||
}
|
||||
```
|
||||
Make your code satisfy the tests in `main()`.
|
|
@ -4,36 +4,4 @@ Create a file-level `private fun f()` and `private val p`. The starter code in
|
|||
`main()` will show you how to write these. Now duplicate `f()` and `p` inside
|
||||
`object Space` and again in `private object Space2`, and get these to work
|
||||
according to the code in `main()`. Notice the changes you need to make so that
|
||||
`f()` is available in `main()`. Compare the different approaches.
|
||||
|
||||
```kotlin
|
||||
// Objects/ObjectEx1.kt
|
||||
package objectex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
private fun f() = trace("f() $p")
|
||||
private val p = "p"
|
||||
|
||||
object Space {
|
||||
fun f() = trace("Space.f() $p")
|
||||
private val p = "Space.p"
|
||||
}
|
||||
|
||||
private object Space2 {
|
||||
fun f() = trace("Space2.f() $p")
|
||||
private val p = "Space2.p"
|
||||
}
|
||||
|
||||
fun main() {
|
||||
f()
|
||||
Space.f()
|
||||
Space2.f()
|
||||
trace eq """
|
||||
f() p
|
||||
Space.f() Space.p
|
||||
Space2.f() Space2.p
|
||||
"""
|
||||
}
|
||||
```
|
||||
`f()` is available in `main()`. Compare the different approaches.
|
|
@ -3,39 +3,4 @@
|
|||
Add more nesting to `ObjectNesting.kt`. Inside both the `Nested` objects, add
|
||||
another `Nested` object that looks the same as its enclosing `Nested`, with the
|
||||
same name and property, and an appropriate `String` for `a`. The code in
|
||||
`main()` provides tests. Notice how the namespaces work.
|
||||
|
||||
```kotlin
|
||||
// Objects/ObjectEx2.kt
|
||||
package objectex2
|
||||
import atomictest.eq
|
||||
|
||||
object Outer {
|
||||
object Nested {
|
||||
val a = "Outer.Nested.a"
|
||||
object Nested {
|
||||
val a = "Outer.Nested.Nested.a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HasObject {
|
||||
object Nested {
|
||||
val a = "HasObject.Nested.a"
|
||||
object Nested {
|
||||
val a = "HasObject.Nested.Nested.a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Outer.Nested.a eq
|
||||
"Outer.Nested.a"
|
||||
HasObject.Nested.a eq
|
||||
"HasObject.Nested.a"
|
||||
Outer.Nested.Nested.a eq
|
||||
"Outer.Nested.Nested.a"
|
||||
HasObject.Nested.Nested.a eq
|
||||
"HasObject.Nested.Nested.a"
|
||||
}
|
||||
```
|
||||
`main()` provides tests. Notice how the namespaces work.
|
|
@ -19,55 +19,4 @@ that returns their class name.
|
|||
|
||||
Now define an `object MonkeysVsSnakes` which is an `AdventureGame`, using the
|
||||
various `create()` functions to initialize it. `populate()` only needs to add
|
||||
one `Monkey` and one `Snake`. The predefined `main()` will test your code.
|
||||
|
||||
```kotlin
|
||||
// Objects/ObjectEx3.kt
|
||||
package objectex3
|
||||
import atomictest.*
|
||||
|
||||
interface AdventureGame {
|
||||
interface Environment
|
||||
interface Character
|
||||
val environment: Environment
|
||||
val characters: MutableList<Character>
|
||||
fun populate()
|
||||
}
|
||||
|
||||
class Jungle : AdventureGame.Environment {
|
||||
object Factory {
|
||||
fun create() = Jungle()
|
||||
}
|
||||
}
|
||||
|
||||
class Monkey : AdventureGame.Character {
|
||||
object Factory {
|
||||
fun create() = Monkey()
|
||||
}
|
||||
override fun toString() = "Monkey"
|
||||
}
|
||||
|
||||
class Snake : AdventureGame.Character {
|
||||
object Factory {
|
||||
fun create() = Snake()
|
||||
}
|
||||
override fun toString() = "Snake"
|
||||
}
|
||||
|
||||
object MonkeysVsSnakes : AdventureGame {
|
||||
override val environment =
|
||||
Jungle.Factory.create()
|
||||
override val characters =
|
||||
mutableListOf<AdventureGame.Character>()
|
||||
override fun populate() {
|
||||
characters.add(Monkey.Factory.create())
|
||||
characters.add(Snake.Factory.create())
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
MonkeysVsSnakes.populate()
|
||||
MonkeysVsSnakes.characters eq
|
||||
"[Monkey, Snake]"
|
||||
}
|
||||
```
|
||||
one `Monkey` and one `Snake`. The predefined `main()` will test your code.
|
|
@ -1,33 +1,3 @@
|
|||
## Polymorphism (#1)
|
||||
|
||||
Add `Hamster` (which says "Squeak!") to `Pet.kt`.
|
||||
|
||||
```kotlin
|
||||
// Polymorphism/PolyExercise1.kt
|
||||
package polymorphismex1
|
||||
import atomictest.eq
|
||||
|
||||
open class Pet {
|
||||
open fun speak() = "Pet"
|
||||
}
|
||||
|
||||
class Dog : Pet() {
|
||||
override fun speak() = "Bark!"
|
||||
}
|
||||
|
||||
class Cat : Pet() {
|
||||
override fun speak() = "Meow"
|
||||
}
|
||||
|
||||
class Hamster : Pet() {
|
||||
override fun speak() = "Squeak!"
|
||||
}
|
||||
|
||||
fun talk(pet: Pet) = pet.speak()
|
||||
|
||||
fun main() {
|
||||
talk(Dog()) eq "Bark!"
|
||||
talk(Cat()) eq "Meow"
|
||||
talk(Hamster()) eq "Squeak!"
|
||||
}
|
||||
```
|
||||
Add `Hamster` (which says "Squeak!") to `Pet.kt`.
|
|
@ -2,169 +2,4 @@
|
|||
|
||||
Add `Wizard`, which implements `Magician`, and `Goblin` which implements
|
||||
`Fighter`, to `FantasyGame.kt`. In `main()`, create and test a `Wizard` named
|
||||
"Max" and a `Goblin` named "Gorbag."
|
||||
|
||||
```kotlin
|
||||
// Polymorphism/PolyExercise2.kt
|
||||
package polymorphismex2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Character {
|
||||
val name: String
|
||||
val type: String
|
||||
fun skills(): String
|
||||
fun play() {
|
||||
trace("$name $type:\n ${skills()}")
|
||||
}
|
||||
}
|
||||
|
||||
interface Magician {
|
||||
fun skills() = "Magic"
|
||||
}
|
||||
|
||||
interface Fighter {
|
||||
fun skills() = "Fighting"
|
||||
}
|
||||
|
||||
interface Flyer {
|
||||
fun skills() = "Flying"
|
||||
}
|
||||
|
||||
open class Elf(override val name: String) :
|
||||
Character, Magician, Flyer {
|
||||
override val type = "Elf"
|
||||
override fun skills() =
|
||||
super<Magician>.skills() + ", " +
|
||||
super<Flyer>.skills()
|
||||
}
|
||||
|
||||
class FightingElf(name: String) :
|
||||
Elf(name), Fighter {
|
||||
override val type = "FightingElf"
|
||||
override fun skills() =
|
||||
super<Elf>.skills() + ", " +
|
||||
super<Fighter>.skills()
|
||||
}
|
||||
|
||||
class Warrior(override val name: String) :
|
||||
Character, Fighter {
|
||||
override val type = "Warrior"
|
||||
override fun skills() = super.skills()
|
||||
}
|
||||
|
||||
class Dragon(override val name: String) :
|
||||
Character, Magician, Flyer {
|
||||
override val type = "Dragon"
|
||||
override fun skills() =
|
||||
super<Magician>.skills() + ", " +
|
||||
super<Flyer>.skills()
|
||||
}
|
||||
|
||||
class Wizard(override val name: String) :
|
||||
Character, Magician {
|
||||
override val type = "Wizard"
|
||||
override fun skills() = super.skills()
|
||||
}
|
||||
|
||||
class Goblin(override val name: String) :
|
||||
Character, Fighter {
|
||||
override val type = "Goblin"
|
||||
override fun skills() = super.skills()
|
||||
}
|
||||
|
||||
fun match(c1: Character, c2: Character) {
|
||||
c1.play()
|
||||
trace("->")
|
||||
c2.play()
|
||||
trace(".")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val characters: List<Character> = listOf(
|
||||
Elf("Titania"),
|
||||
FightingElf("Legolas"),
|
||||
Warrior("Conan"),
|
||||
Dragon("Puff"),
|
||||
Wizard("Max"),
|
||||
Goblin("Gorbag")
|
||||
)
|
||||
characters.forEach { c1 ->
|
||||
characters.filter { it != c1 }
|
||||
.forEach { c2 -> match(c1, c2) }
|
||||
}
|
||||
trace eq """
|
||||
Titania Elf: Magic, Flying ->
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting .
|
||||
Titania Elf: Magic, Flying ->
|
||||
Conan Warrior: Fighting .
|
||||
Titania Elf: Magic, Flying ->
|
||||
Puff Dragon: Magic, Flying .
|
||||
Titania Elf: Magic, Flying ->
|
||||
Max Wizard: Magic .
|
||||
Titania Elf: Magic, Flying ->
|
||||
Gorbag Goblin: Fighting .
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting ->
|
||||
Titania Elf: Magic, Flying .
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting ->
|
||||
Conan Warrior: Fighting .
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting ->
|
||||
Puff Dragon: Magic, Flying .
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting ->
|
||||
Max Wizard: Magic .
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting ->
|
||||
Gorbag Goblin: Fighting .
|
||||
Conan Warrior: Fighting ->
|
||||
Titania Elf: Magic, Flying .
|
||||
Conan Warrior: Fighting ->
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting .
|
||||
Conan Warrior: Fighting ->
|
||||
Puff Dragon: Magic, Flying .
|
||||
Conan Warrior: Fighting ->
|
||||
Max Wizard: Magic .
|
||||
Conan Warrior: Fighting ->
|
||||
Gorbag Goblin: Fighting .
|
||||
Puff Dragon: Magic, Flying ->
|
||||
Titania Elf: Magic, Flying .
|
||||
Puff Dragon: Magic, Flying ->
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting .
|
||||
Puff Dragon: Magic, Flying ->
|
||||
Conan Warrior: Fighting .
|
||||
Puff Dragon: Magic, Flying ->
|
||||
Max Wizard: Magic .
|
||||
Puff Dragon: Magic, Flying ->
|
||||
Gorbag Goblin: Fighting .
|
||||
Max Wizard: Magic ->
|
||||
Titania Elf: Magic, Flying .
|
||||
Max Wizard: Magic ->
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting .
|
||||
Max Wizard: Magic ->
|
||||
Conan Warrior: Fighting .
|
||||
Max Wizard: Magic ->
|
||||
Puff Dragon: Magic, Flying .
|
||||
Max Wizard: Magic ->
|
||||
Gorbag Goblin: Fighting .
|
||||
Gorbag Goblin: Fighting ->
|
||||
Titania Elf: Magic, Flying .
|
||||
Gorbag Goblin: Fighting ->
|
||||
Legolas FightingElf:
|
||||
Magic, Flying, Fighting .
|
||||
Gorbag Goblin: Fighting ->
|
||||
Conan Warrior: Fighting .
|
||||
Gorbag Goblin: Fighting ->
|
||||
Puff Dragon: Magic, Flying .
|
||||
Gorbag Goblin: Fighting ->
|
||||
Max Wizard: Magic .
|
||||
"""
|
||||
}
|
||||
```
|
||||
"Max" and a `Goblin` named "Gorbag."
|
|
@ -9,42 +9,4 @@ From `Base`, inherit a class called `Derived`. Override `f1()`, first tracing
|
|||
the same way.
|
||||
|
||||
In `main()`, create a `Derived` object and upcast it to a `Base` reference.
|
||||
Call `f1()` for this reference and explain the resulting trace.
|
||||
|
||||
```kotlin
|
||||
// Polymorphism/PolyExercise3.kt
|
||||
package polymorphism
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
open class Base {
|
||||
open fun f1() {
|
||||
trace("Base.f1")
|
||||
f2()
|
||||
}
|
||||
open fun f2() = trace("Base.f2")
|
||||
}
|
||||
|
||||
class Derived : Base() {
|
||||
override fun f1() {
|
||||
trace("Derived.f1")
|
||||
super.f1()
|
||||
}
|
||||
override fun f2() {
|
||||
trace("Derived.f2")
|
||||
super.f2()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val base: Base = Derived()
|
||||
base.f1()
|
||||
trace eq """
|
||||
Derived.f1
|
||||
Base.f1
|
||||
Derived.f2
|
||||
Base.f2
|
||||
"""
|
||||
}
|
||||
```
|
||||
Call `f1()` for this reference and explain the resulting trace.
|
|
@ -5,69 +5,4 @@ the `Transport` class and add `trace("size ${transport.capacity}")` after the
|
|||
`when` expression in `travel()`. Then add new subclasses called `Tram`
|
||||
containing a `val route: String` and `Plane` containing a `val flightNumber:
|
||||
String`. Modify `travel()` to accomodate these new subclasses. Add `Tram` and
|
||||
`Plane` to the `List` in `main()`.
|
||||
|
||||
```kotlin
|
||||
// SealedClasses/SealedEx1.kt
|
||||
package sealedclassesex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
sealed class Transport {
|
||||
abstract val capacity: Int
|
||||
}
|
||||
|
||||
data class Train(
|
||||
val line: String,
|
||||
override val capacity: Int
|
||||
) : Transport()
|
||||
|
||||
data class Bus(
|
||||
val number: String,
|
||||
override val capacity: Int
|
||||
) : Transport()
|
||||
|
||||
data class Tram(
|
||||
val route: String,
|
||||
override val capacity: Int
|
||||
) : Transport()
|
||||
|
||||
data class Plane(
|
||||
val flightNumber: String,
|
||||
override val capacity: Int
|
||||
) : Transport()
|
||||
|
||||
fun travel(transport: Transport) {
|
||||
when (transport) {
|
||||
is Train ->
|
||||
trace("Train ${transport.line}")
|
||||
is Bus ->
|
||||
trace("Bus ${transport.number}")
|
||||
is Tram ->
|
||||
trace("Tram ${transport.route}")
|
||||
is Plane ->
|
||||
trace("Plane ${transport.flightNumber}")
|
||||
}
|
||||
trace("size ${transport.capacity}")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
listOf(
|
||||
Train("S1", 200),
|
||||
Bus("11", 45),
|
||||
Tram("22A Express", 60),
|
||||
Plane("1103", 190)
|
||||
).forEach(::travel)
|
||||
trace eq """
|
||||
Train S1
|
||||
size 200
|
||||
Bus 11
|
||||
size 45
|
||||
Tram 22A Express
|
||||
size 60
|
||||
Plane 1103
|
||||
size 190
|
||||
"""
|
||||
}
|
||||
```
|
||||
`Plane` to the `List` in `main()`.
|
|
@ -1,24 +1,4 @@
|
|||
## Sealed Classes (#2)
|
||||
|
||||
Use `sealedSubclasses` to iterate through the classes in the previous
|
||||
exercise. For each class, `trace()` its `simpleName`.
|
||||
|
||||
```kotlin
|
||||
// SealedClasses/SealedEx2.kt
|
||||
package sealedclassesex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
fun main() {
|
||||
Transport::class.sealedSubclasses
|
||||
.map { it.simpleName }
|
||||
.forEach { trace(it) }
|
||||
trace eq """
|
||||
Train
|
||||
Bus
|
||||
Tram
|
||||
Plane
|
||||
"""
|
||||
}
|
||||
```
|
||||
exercise. For each class, `trace()` its `simpleName`.
|
|
@ -3,27 +3,4 @@
|
|||
Modify `SealedSubclasses.kt` so that all the subclasses of `Top` are nested
|
||||
within `Top`. Create a seeded random-number generator by defining `val rand =
|
||||
Random(17)`. Use this generator to randomly select a subclass of `Top` and
|
||||
display its `simpleName`.
|
||||
|
||||
```kotlin
|
||||
// SealedClasses/SealedEx3.kt
|
||||
package sealedclassesex3
|
||||
import atomictest.eq
|
||||
import kotlin.random.Random
|
||||
|
||||
sealed class Top {
|
||||
class Middle1 : Top()
|
||||
class Middle2 : Top()
|
||||
open class Middle3 : Top()
|
||||
class Bottom3 : Middle3()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Top::class.sealedSubclasses
|
||||
.map { it.simpleName } eq
|
||||
"[Middle1, Middle2, Middle3]"
|
||||
val rand = Random(17)
|
||||
Top::class.sealedSubclasses
|
||||
.random(rand).simpleName eq "Middle3"
|
||||
}
|
||||
```
|
||||
display its `simpleName`.
|
|
@ -11,54 +11,4 @@ Show that the compiler won't allow you to call `jump()`.
|
|||
|
||||
In `main()`, create an instance of `Mouse` and `KangarooRat`, and show that
|
||||
you can call `jump()` for the latter. Pass both objects into `upcast()`, and
|
||||
use `trace` to verify the output.
|
||||
|
||||
```kotlin
|
||||
// Upcasting/UpcastExercise1.kt
|
||||
package upcasting
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Rodent {
|
||||
fun eat()
|
||||
fun speak()
|
||||
}
|
||||
|
||||
class Mouse: Rodent {
|
||||
override fun eat() =
|
||||
trace("Mouse.eat")
|
||||
override fun speak() =
|
||||
trace("Mouse.speak")
|
||||
}
|
||||
|
||||
class KangarooRat: Rodent {
|
||||
override fun eat() =
|
||||
trace("KangarooRat.eat")
|
||||
override fun speak() =
|
||||
trace("KangarooRat.speak")
|
||||
fun jump() =
|
||||
trace("KangarooRat.jump")
|
||||
}
|
||||
|
||||
fun upcast(rodent: Rodent) {
|
||||
rodent.eat()
|
||||
rodent.speak()
|
||||
// rodent.jump() // Won't compile
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val mouse = Mouse()
|
||||
val kangarooRat = KangarooRat()
|
||||
kangarooRat.jump()
|
||||
upcast(mouse)
|
||||
upcast(kangarooRat)
|
||||
trace eq """
|
||||
KangarooRat.jump
|
||||
Mouse.eat
|
||||
Mouse.speak
|
||||
KangarooRat.eat
|
||||
KangarooRat.speak
|
||||
"""
|
||||
}
|
||||
```
|
||||
use `trace` to verify the output.
|
|
@ -16,61 +16,4 @@ From `Apple`, inherit `GrannySmith`, `Gala`, `Fuji` and `Braeburn`. Override
|
|||
`bite Gala`, `press Fuji` and `peel Braeburn`.
|
||||
|
||||
In `main()`, make a `List<Apple>` and populate it with the specific types of
|
||||
`Apple`. Iterate through the list calling `consume()` for each object.
|
||||
|
||||
```kotlin
|
||||
// Upcasting/UpcastExercise2.kt
|
||||
package upcasting
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
private var counter = 0
|
||||
|
||||
abstract class Apple(val type: String) {
|
||||
private val id = counter++
|
||||
init {
|
||||
trace("$type $id")
|
||||
}
|
||||
abstract fun consume()
|
||||
}
|
||||
|
||||
class GrannySmith : Apple("GrannySmith") {
|
||||
override fun consume() =
|
||||
trace("chomp $type")
|
||||
}
|
||||
|
||||
class Gala : Apple("Gala") {
|
||||
override fun consume() =
|
||||
trace("bite $type")
|
||||
}
|
||||
|
||||
class Fuji : Apple("Fuji") {
|
||||
override fun consume() =
|
||||
trace("press $type")
|
||||
}
|
||||
|
||||
class Braeburn : Apple("Braeburn") {
|
||||
override fun consume() =
|
||||
trace("peel $type")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val apples: List<Apple> = listOf(
|
||||
GrannySmith(),
|
||||
Gala(),
|
||||
Fuji(),
|
||||
Braeburn()
|
||||
)
|
||||
apples.forEach { it.consume() }
|
||||
trace eq """
|
||||
GrannySmith 0
|
||||
Gala 1
|
||||
Fuji 2
|
||||
Braeburn 3
|
||||
chomp GrannySmith
|
||||
bite Gala
|
||||
press Fuji
|
||||
peel Braeburn
|
||||
"""
|
||||
}
|
||||
```
|
||||
`Apple`. Iterate through the list calling `consume()` for each object.
|
|
@ -19,55 +19,4 @@ so that each function calls the member function avalable to its parameter.
|
|||
In `main()`, make a `Hero` object and pass it to each of the functions `t()`,
|
||||
`u()`, `v()`, and `w()` and validate the output using `trace`. Notice how
|
||||
passing a `Hero` to each of the functions upcasts it so that, inside the
|
||||
function, the `Hero` becomes only the type of that parameter.
|
||||
|
||||
|
||||
```kotlin
|
||||
// Upcasting/UpcastExercise3.kt
|
||||
package upcasting
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Fight {
|
||||
fun fight()
|
||||
}
|
||||
|
||||
interface Swim {
|
||||
fun swim()
|
||||
}
|
||||
|
||||
interface Fly {
|
||||
fun fly()
|
||||
}
|
||||
|
||||
open class ActionCharacter {
|
||||
fun fight() =
|
||||
trace("ActionCharacter fight")
|
||||
}
|
||||
|
||||
class Hero :
|
||||
ActionCharacter(), Fight, Swim, Fly {
|
||||
override fun swim() = trace("Hero swim")
|
||||
override fun fly() = trace("Hero fly")
|
||||
}
|
||||
|
||||
fun t(x: Fight) = x.fight()
|
||||
fun u(x: Swim) = x.swim()
|
||||
fun v(x: Fly) = x.fly()
|
||||
fun w(x: ActionCharacter) = x.fight()
|
||||
|
||||
fun main() {
|
||||
val h = Hero()
|
||||
t(h) // Treat it as a Fight
|
||||
u(h) // Treat it as a Swim
|
||||
v(h) // Treat it as a Fly
|
||||
w(h) // Treat it as an ActionCharacter
|
||||
trace eq """
|
||||
ActionCharacter fight
|
||||
Hero swim
|
||||
Hero fly
|
||||
ActionCharacter fight
|
||||
"""
|
||||
}
|
||||
```
|
||||
function, the `Hero` becomes only the type of that parameter.
|
Loading…
Reference in New Issue