Skip to content

Commit 953132a

Browse files
committed
update delegation section
1 parent f483e32 commit 953132a

File tree

1 file changed

+135
-38
lines changed

1 file changed

+135
-38
lines changed

docs/topics/tour/kotlin-tour-intermediate-classes-interfaces.md

Lines changed: 135 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ only support single inheritance. If you need to inherit from multiple sources, c
155155
Interfaces are similar to classes, but they have some differences:
156156

157157
* You can't create an instance of an interface. They don't have a constructor or header.
158-
* Their functions and properties are implicitly inheritable by default. In Kotlin, we say that they are "open".
158+
* Their functions and properties are implicitly inheritable by default. In Kotlin, we say that they are "open."
159159
* You don't need to mark their functions as `abstract` if you don't give them an implementation.
160160

161161
Similar to abstract classes, you use interfaces to define a set of functions and properties that classes can inherit and
@@ -269,87 +269,184 @@ For more information about interfaces and interface inheritance, see [Interfaces
269269

270270
## Delegation
271271

272-
Interfaces are useful, but if your interface contains many functions, child classes may end up with a lot of
273-
boilerplate code. When you only want to override a small part of your parent's behavior, you need to repeat yourself a lot.
272+
Interfaces are useful, but if your interface contains many functions, its child classes can end up with a lot of
273+
boilerplate code. If you only want to override a small part of a class's behavior, you need to repeat yourself a lot.
274274

275275
> Boilerplate code is a chunk of code that is reused with little or no alteration in multiple parts of a software project.
276276
>
277277
{style="tip"}
278278

279-
For example, let's say that you have an interface called `Drawable` that contains a number of functions and one property
279+
For example, let's say that you have an interface called `DrawingTool` that contains a number of functions and one property
280280
called `color`:
281281

282282
```kotlin
283-
interface Drawable {
284-
fun draw()
285-
fun resize()
286-
val color: String?
283+
interface DrawingTool {
284+
val color: String
285+
fun draw(shape: String)
286+
fun erase(area: String)
287+
fun getToolInfo(): String
287288
}
288289
```
289290

290-
You create a class called `Circle` which implements the `Drawable` interface and provides implementations for all of
291+
You create a class called `PenTool` which implements the `DrawingTool` interface and provides implementations for all of
291292
its members:
292293

293294
```kotlin
294-
class Circle : Drawable {
295-
override fun draw() {
296-
TODO("An example implementation")
295+
class PenTool : DrawingTool {
296+
override val color: String = "black"
297+
298+
override fun draw(shape: String) {
299+
println("Drawing $shape using a pen in $color")
297300
}
298-
299-
override fun resize() {
300-
TODO("An example implementation")
301+
302+
override fun erase(area: String) {
303+
println("Erasing $area with pen tool")
304+
}
305+
306+
override fun getToolInfo(): String {
307+
return "PenTool(color=$color)"
301308
}
302-
override val color = null
303309
}
304310
```
305311

306-
If you wanted to create a child class of the `Circle` class which had the same behavior **except** for the value of the
307-
`color` property, you still need to add implementations for each member function of the `Circle` class:
312+
You want to create a class like `PenTool` with the same behavior but a different `color` value.
313+
One approach is to create a new class that expects an object implementing the `DrawingTool` interface as a parameter,
314+
like a `PenTool` class instance. Then, inside the class, you can override the `color` property.
315+
316+
But in this scenario, you need to add implementations for each member of the `DrawingTool` interface:
308317

309318
```kotlin
310-
class RedCircle(val circle: Circle) : Circle {
319+
interface DrawingTool {
320+
val color: String
321+
fun draw(shape: String)
322+
fun erase(area: String)
323+
fun getToolInfo(): String
324+
}
325+
326+
class PenTool : DrawingTool {
327+
override val color: String = "black"
328+
329+
override fun draw(shape: String) {
330+
println("Drawing $shape using a pen in $color")
331+
}
332+
333+
override fun erase(area: String) {
334+
println("Erasing $area with pen tool")
335+
}
336+
337+
override fun getToolInfo(): String {
338+
return "PenTool(color=$color)"
339+
}
340+
}
341+
//sampleStart
342+
class CanvasSession(val tool: DrawingTool) : DrawingTool {
343+
override val color: String = "blue"
311344

312-
// Start of boilerplate code
313-
override fun draw() {
314-
circle.draw()
345+
override fun draw(shape: String) {
346+
tool.draw(shape)
315347
}
316348

317-
override fun resize() {
318-
circle.resize()
349+
override fun erase(area: String) {
350+
tool.erase(area)
319351
}
320352

321-
// End of boilerplate code
322-
override val color = "red"
353+
override fun getToolInfo(): String {
354+
return tool.getToolInfo()
355+
}
356+
}
357+
//sampleEnd
358+
fun main() {
359+
val pen = PenTool()
360+
val session = CanvasSession(pen)
361+
362+
println("Pen color: ${pen.color}")
363+
// Pen color: black
364+
365+
println("Session color: ${session.color}")
366+
// Session color: blue
367+
368+
session.draw("circle")
369+
// Drawing circle with pen in black
370+
371+
session.erase("top-left corner")
372+
// Erasing top-left corner with pen tool
373+
374+
println(session.getToolInfo())
375+
// PenTool(color=black)
323376
}
324377
```
378+
{kotlin-runnable="true" id="kotlin-tour-interface-non-delegation"}
325379

326-
You can see that if you have a large number of member functions in the `Drawable` interface, the amount of boilerplate
327-
code in the `RedCircle` class can be very large. However, there is an alternative.
380+
You can see that if you have a large number of member functions in the `DrawingTool` interface, the amount of boilerplate
381+
code in the `CanvasSession` class can be large. However, there is an alternative.
328382

329-
In Kotlin, you can use delegation to delegate the interface implementation to an instance of a class. For example,
330-
you can create an instance of the `Circle` class and delegate the implementations of the member functions of the `Circle`
331-
class to this instance. To do this, use the `by` keyword. For example:
383+
In Kotlin, you can delegate the interface implementation to a class instance using the `by` keyword. For example:
332384

333385
```kotlin
334-
class RedCircle(param: Circle) : Drawable by param
386+
class CanvasSession(val tool: DrawingTool) : DrawingTool by tool
335387
```
336388

337-
Here, `param` is the name of the instance of the `Circle` class that the implementations of member functions are delegated to.
389+
Here, `tool` is the name of the `PenTool` class instance where the implementations of member functions are delegated to.
338390

339-
Now you don't have to add implementations for the member functions in the `RedCircle` class. The compiler does
340-
this for you automatically from the `Circle` class. This saves you from having to write a lot of boilerplate code. Instead,
391+
Now you don't have to add implementations for the member functions in the `CanvasSession` class. The compiler does
392+
this for you automatically from the `PenTool` class. This saves you from having to write a lot of boilerplate code. Instead,
341393
you add code only for the behavior you want to change for your child class.
342394

343395
For example, if you want to change the value of the `color` property:
344396

345397
```kotlin
346-
class RedCircle(param : Circle) : Drawable by param {
398+
interface DrawingTool {
399+
val color: String
400+
fun draw(shape: String)
401+
fun erase(area: String)
402+
fun getToolInfo(): String
403+
}
404+
405+
class PenTool : DrawingTool {
406+
override val color: String = "black"
407+
408+
override fun draw(shape: String) {
409+
println("Drawing $shape using a pen in $color")
410+
}
411+
412+
override fun erase(area: String) {
413+
println("Erasing $area with pen tool")
414+
}
415+
416+
override fun getToolInfo(): String {
417+
return "PenTool(color=$color)"
418+
}
419+
}
420+
421+
//sampleStart
422+
class CanvasSession(val tool: DrawingTool) : DrawingTool by tool {
347423
// No boilerplate code!
348-
override val color = "red"
424+
override val color: String = "blue"
425+
}
426+
//sampleEnd
427+
fun main() {
428+
val pen = PenTool()
429+
val session = CanvasSession(pen)
430+
431+
println("Pen color: ${pen.color}")
432+
// Pen color: black
433+
434+
println("Session color: ${session.color}")
435+
// Session color: blue
436+
437+
session.draw("circle")
438+
// Drawing circle with pen in black
439+
440+
session.erase("top-left corner")
441+
// Erasing top-left corner with pen tool
442+
443+
println(session.getToolInfo())
444+
// PenTool(color=black)
349445
}
350446
```
447+
{kotlin-runnable="true" id="kotlin-tour-interface-delegation"}
351448

352-
If you want to, you can also override the behavior of an inherited member function in the `RedCircle` class, but now
449+
If you want to, you can also override the behavior of an inherited member function in the `CanvasSession` class, but now
353450
you don't have to add new lines of code for every inherited member function.
354451

355452
For more information, see [Delegation](delegation.md).

0 commit comments

Comments
 (0)