0% found this document useful (0 votes)
2 views4 pages

Advancedkotlin 8

The document discusses advanced aspects of Kotlin programming, focusing on interfaces, object expressions, generics, scope functions, and the builder pattern. It provides examples of how to implement these features, such as managing NPCs in a game and building complex objects using a builder class. Additionally, it highlights the use of companion objects and singleton patterns in Kotlin.

Uploaded by

al397126
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views4 pages

Advancedkotlin 8

The document discusses advanced aspects of Kotlin programming, focusing on interfaces, object expressions, generics, scope functions, and the builder pattern. It provides examples of how to implement these features, such as managing NPCs in a game and building complex objects using a builder class. Additionally, it highlights the use of companion objects and singleton patterns in Kotlin.

Uploaded by

al397126
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

Introduction

I We will comment on some aspects of Kotlin that facilitate


programming

VJ1229, Mobile Device Applications I We will see:


• Interfaces
• Some uses of the object keyword
Advanced Aspects of Kotlin • Some aspects of generics
• Scope functions
• The builder pattern
©2025, Juan Miguel Vilar Torres

1/1 2/31

Interfaces Example
We want to manage the NPCs for a game:

data class Point(val x: Int, val y: Int)

interface NPC {
I Declare abstract functions and functions implementations var location: Point
fun move()
I Can have abstract properties é Cannot store state fun draw()
}

I If I is an interface, the variable class ManageNPCs {


val NPCs = ArrayList<NPC>()
var v: I
accepts objects of any class that implements the interface I fun addNPC(npc: NPC) = NPCs.add(npc)

fun updateScreen() {
for (npc in NPCs) {
npc.move()
npc.draw()
}
}
}
3/31 4/31

Example (2) Some Uses


Now we can declare our NPCs like this:

class Vampire(val name: String,


override var location: Point) : NPC { I Group classes with similar characteristics
override fun move() {
// .. I Allow the use of Collections with elements of different types
}
I Isolate one class from the implementation of other classes
override fun draw() {
// .. I Ease testing
}
}

// ..
val manager = ManageNPCs()
manager.addNPC(Vampire("Dracula", Point(0, 10)))

5/31 6/31

Object Expressions An Example

interface StringProcessor {
fun transform(s: String): String
8
< of an interface }
I Used when you need an object
: of a slightly different class fun main () {
val duplicator = object : StringProcessor {
and you don’t want to write a new class
override fun transform(s: String): String {
I They are written as the object keyword followed by a colon, return s + s
the interface or parent class, and the body }
}

val doubleGreeting = duplicator.transform("Hello")


}

7/31 8/31
Object for Singletons Example
object Identifiers {
private var counter = 0

fun getNumber() = counter++


I Used when there must be only one instance of a class
fun getId() = "id${getNumber()}"
I Eg: a single object to access a database, to access the
}
internet, etc
fun main () {
I Implemented with the object keyword and a name
val n = Identifiers.getNumber()
val m = Identifiers.getNumber()
I Don’t abuse it é it is similar to a global variable
println("Two numbers: $n, $m") // 0, 1

val st1 = Identifiers.getId()


val st2 = Identifiers.getId()
println("Two strings: $st1, $st2") // id2, id3
}
9/31 10/31

Companion Objects Example

class Vampire(val name: String,


I A companion object is an object that can be associated to a override var location: Point) : NPC {
class // ..

I It allows the definition of attributes accessed through the class companion object {
name, no need to use an instance const val INITIAL_LIFE = 10
I Typical use: defining constants, factory methods, etc const val FIGHT_STRENGTH = 20
}
}

11/31 12/31

Generics Example

I Allow the parameterization of a class, function or interface


interface Stack<T> {
with a type val length : Int
I The type parameter is marked using <T> (where T is the name val isEmpty : Boolean
of the parameter) fun push(item : T)
fun pop(): T
I Very useful for collections }

13/31 14/31

Generic Functions Scope Functions

I They are used to apply some functions to an object


I E.g. in the class ManageNPCs we could have written
I Generic functions are marked with <T> before the class name:
updateScreen like this:
fun <T> mutableListOf(vararg elements: T): MutableList<T>

I When used, the type parameter can be omitted if it is inferred fun updateScreen() { fun updateScreen() {
for (npc in NPCs) { for (npc in NPCs)
from the context:
npc.move() with (npc) {
val list = mutableListOf(1, 2, 3) npc.draw() move()
} draw()
} }
}

15/31 16/31
Scope Functions (2) apply

I Applies some functions to an object and returns it


I Two axis of classification:
I This
• The object is accessed as this or it
return Vampire("Dracula", Point(2, 3)).apply {
• The return is the object or the value of the lambda
move()
I Available functions: draw()
}
Returns
Access I is equivalent to
Object Lambda
val vampire = Vampire("Dracula", Point(2, 3))
this apply run, with vampire.move()
it also let vampire.draw()
return vampire

17/31 18/31

run with

I Similar to apply but returns the result of the lambda I Similar to run but the object is received as a parameter

I This I This
val currentPosition = vampire.run { val currentPosition = with (vampire) {
move() move()
draw() draw()
position position
} }

I is equivalent to I is equivalent to
vampire.move() vampire.move()
vampire.draw() vampire.draw()
val currentPosition = vampire.position val currentPosition = vampire.position

19/31 20/31

also let
I Similar to apply but the object is accessed through it
I Similar to also but the result is the value of the lambda
I This
I This
return Vampire("Dracula", Point(2,3)).also {
it.move() val currentPosition = vampire.let {
it.draw() it.move()
println("The name of the vampire is ${it.name}") println("The name of the vampire is ${it.name}")
} it.position
}
I is equivalent to
I is equivalent to
val vampire = Vampire("Dracula", Point(2,3))
vampire.move() vampire.move()
vampire.draw() println("The name of the vampire is ${vampire.name}")
println("The name of the vampire is ${vampire.name}") val currentPosition = vampire.position
return vampire

21/31 22/31

The Builder Pattern The Screen Class

I Suppose we have a class for storing the information about a


I Used to build complex objects
screen in the game:
I Offers functions to add components or change options as class Screen (val dayLight: Double,
needed val lightSources: List<LightSource>,
I It usually uses fluid interfaces: val NPCs: List<NPC>,
val buildings: List<Building>
• The functions return the same object ) {
• It can be used as a (limited) DSL // ...
}
I It usually has a function that returns the object that has been
built I Creating and filling the parameters of the constructor will be
cumbersome

23/31 24/31
The Builder Class The Builder Class (2)

And we add a function for each of the elements that can be added:
We create a class to incrementally build all the parameters:
fun setDayLight(dayLight: Double): ScreenBuilder {
class ScreenBuilder { this.dayLight = dayLight
private var dayLight: Double = 0.0 return this
private var lightSources = ArrayList<LightSource>() }
private var NPCs = ArrayList<NPC>()
Or, using apply:
private var buildings = ArrayList<Building>()
fun setDayLight(dayLight: Double) =
apply { this.dayLight = dayLight }

25/31 26/31

The Builder Class (3) The Builder Class (4)

The rest of elements that can be added:

fun addLightSource(lightSource: LightSource) = And a final function to actually build the screen
apply { lightSources.add(lightSource) }
fun createScreen() = Screen(dayLight,
fun addNPC(npc: NPC) = lightSources,
apply { NPCs.add(npc)} NPCs,
buildings)
fun addBuilding(building: Building) =
apply { buildings.add(building) }

27/31 28/31

Example of usage Example of usage (2)


Since each function returned the builder, they can be concatenated
like this:
Using scope functions, we can even shorten this code:
fun mainScreen(): Screen {
val builder = ScreenBuilder() fun mainScreen(): Screen = with(ScreenBuilder()) {
setDayLight(12.0)
builder.setDayLight(12.0) addLightSource(LightSource(Point(10, 20), 2.4))
.addLightSource(LightSource(Point(10, 20), 2.4)) addBuilding(Building("Saloon"))
.addBuilding(Building("Saloon")) addBuilding(Building("Drugstore"))
.addBuilding(Building("Drugstore")) addNPC(Vampire("Dracula", Point(100, 100)))
.addNPC(Vampire("Dracula", Point(100, 100))) createScreen()
}
return builder.createScreen()
}

29/31 30/31

Further Information

I In the Kotlin book

I Chapter “Abstract Classes and Interfaces”

I Chapter “The object Keyword”

I Chapter “Scope Functions”

31/31

You might also like