Scope functions in Kotlin zijn vijf functies van de standaardbibliotheek (let, run, with, apply, also) die een codeblok uitvoeren in de context van een object. Ze verschillen in de manier van toegang tot het object (via it of this) en de retourwaarde (contextobject of resultaat van de lambda). Volgens Kotlin documentatie (2026) vermindert de juiste keuze van scope function de boilerplate-code met 25–40%. Scope functions helpen bij het schrijven van initialisatie, configuratie en transformaties van objecten in declaratieve stijl.
Belangrijkste punten
Scope functions (bereikfuncties) — een set van vijf functies (let, run, with, apply, also) ingebouwd in de standaardbibliotheek van Kotlin. Elk van hen neemt een lambda en wijzigt tijdelijk het bereik zodat de code binnen de lambda wordt uitgevoerd in de context van een specifiek object. Dit voorkomt herhaling van de objectnaam bij meerdere aanroepen en groepeert gerelateerde bewerkingen in één blok.
Twee belangrijke kenmerken onderscheiden scope functions: de manier van toegang tot het contextobject (via this of via it) en de retourwaarde (het contextobject zelf of het resultaat van de lambda). Toegang via this maakt het object een impliciete ontvanger — methoden en eigenschappen worden zonder prefix aangeroepen. Toegang via it geeft het object door als expliciete parameter van de lambda. Het retourneren van het object maakt het bouwen van ketens mogelijk, het retourneren van het resultaat — het toewijzen van de getransformeerde waarde.
Historisch gezien verschenen scope functions als vervanging van het klassieke Builder-patroon en tijdelijke variabelen voor initialisatie. In plaats van het maken van een aparte builder-klasse schrijft de ontwikkelaar apply { eigenschap1 = ...; eigenschap2 = ... }. Dit vermindert de hoeveelheid code en maakt initialisatie leesbaarder. In het Kotlin-ecosysteem worden scope functions in alle frameworks en bibliotheken gebruikt als een standaard idioom van de taal.
De keuze van de juiste scope function wordt bepaald door twee vragen: wilt u this of it gebruiken voor toegang tot het object, en wilt u het object zelf of het resultaat van de lambda retourneren. De combinatie van twee binaire kenmerken geeft vier mogelijke varianten, en with neemt een speciale plaats in als de enige functie die geen extensie is.
| Functie | Toegang | Retour | Extensie? | Typisch scenario |
|---|---|---|---|---|
| let | it | resultaat lambda | ja | Transformatie, null-controle |
| run | this | resultaat lambda | ja | Berekening met context |
| with | this | resultaat lambda | nee | Groeperen van aanroepen |
| apply | this | contextobject | ja | Initialisatie van eigenschappen |
| also | it | contextobject | ja | Bijwerkingen, loggen |
Voor configuratie van een object zonder het retourneren van een nieuwe waarde gebruikt u apply (toegang via this) of also (toegang via it). Voor het berekenen van een nieuwe waarde op basis van het object gebruikt u let (toegang via it) of run (toegang via this). Voor het groeperen van aanroepen zonder koppeling gebruikt u with. Het volgen van dit schema maakt de code voorspelbaar voor andere ontwikkelaars die bekend zijn met Kotlin.
apply — scope function die het contextobject retourneert en er toegang toe biedt via this. Het is het ideale hulpmiddel voor het initialiseren van eigenschappen van een object na creatie. Binnen het apply-blok kunnen eigenschappen worden ingesteld zonder herhaling van de objectnaam. De methode retourneert het object zelf, waardoor apply in ketens kan worden ingebouwd of in initialisatoren kan worden gebruikt.
data class ServerConfig(
var host: String = "localhost",
var port: Int = 8080,
var useTls: Boolean = false
)
val config = ServerConfig().apply {
host = "api.example.com"
port = 443
useTls = true
}
In de listing configureert apply ServerConfig door rechtstreeks via this toegang te krijgen tot eigenschappen (this weggelaten). Zonder apply zou men config.host = ..., config.port = ... moeten schrijven — drie herhaalde verwijzingen naar dezelfde variabele. Het retourneren van het object maakt het mogelijk het configuratieresultaat op te slaan in val config. also geeft, in tegenstelling tot apply, het object door via it en is geschikt voor bijwerkingen.
fun saveUser(user: User) {
validate(user).also { result ->
println("Validatieresultaat: $result")
}
val savedUser = user.also {
log("Saving user ${it.id}")
database.save(it)
}
}
In het voorbeeld wordt also gebruikt voor loggen — door het originele object te retourneren, voert het een bijwerking uit (bericht weergeven) zonder het object te wijzigen. Dit is een keten: validate(user) retourneert User, also logt het resultaat en geeft de gebruiker door. also is ook handig voor het toevoegen van elementen aan verzamelingen of debuggen in het midden van een transformatieketen.
let — scope function die het resultaat van de lambda retourneert en de context doorgeeft via it. Dit is de primaire functie voor null-veilige transformaties: als het object nullable is, voert de combinatie ?.let { } het blok alleen uit bij een niet-null waarde. let wordt ook gebruikt voor het beperken van het bereik van tijdelijke variabelen en map-achtige transformaties van het ene type naar het andere.
fun findUser(id: Int): User?
val displayName = findUser(42)?.let { user ->
"${user.name} (${user.email})"
} ?: "Unknown user"
// let met collectietransformatie
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
it.toIntOrNull()
} ?: 0
In het eerste voorbeeld wordt let aangeroepen via ?. — het blok wordt alleen uitgevoerd als findUser niet-null heeft geretourneerd. De lambda ontvangt user via it (kan voor de duidelijkheid worden hernoemd) en retourneert een geformatteerde string. Als de gebruiker niet is gevonden — geeft de Elvis-operator ?: een standaardwaarde. In het tweede voorbeeld transformeert let een string naar een getal met mogelijke null.
run — analoog aan let met toegang via this, die het resultaat van de lambda retourneert. run is handig voor berekeningen waarbij de context van het object nodig is, maar een nieuwe waarde wordt geretourneerd. Een run()-aanroep zonder object (zoals run { ... }) creëert een tijdelijk bereik voor variabelen. with — geen extensie met toegang via this, geschikt voor het groeperen van aanroepen zonder koppeling.
// run als contextgestuurde berekening
val transformed = listOf(1, 2, 3).run {
filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]
// with voor groeperen van aanroepen
val info = with(StringBuilder()) {
append("Name: ")
append("Alice")
toString()
}
In het voorbeeld retourneert run op een lijst het resultaat van filteren en mappen — de transformatie zelf, niet de lijst. with neemt StringBuilder als argument (niet via een punt) en werkt intern met this: append, append, toString. Het resultaat van with — de string info. De keuze tussen run en with is vaak subjectief, maar with heeft de voorkeur wanneer het object van een andere plaats wordt doorgegeven zonder aanroepketen.
with verschilt van de andere scope functions doordat het geen extensiefunctie is. Het neemt het object als eerste argument en de lambda als tweede. Binnen de lambda is het object toegankelijk via this. with is handig voor het groeperen van methodeaanroepen van het object zonder het resultaat op te slaan of wanneer tijdelijk “in de context” moet worden gegaan om meerdere eigenschappen te lezen.
val user = User("Alice", "alice@example.com", "Admin")
val summary = with(user) {
"User $name has role $role and email $email"
}
println(summary)
// with voor initialisatie van UI-component
with(TextView(context)) {
text = "Hello"
textSize = 18.0f
setTextColor(Color.BLUE)
}
In het eerste voorbeeld vormt with de string summary door rechtstreeks via this toegang te krijgen tot de eigenschappen van user. Zonder with zou men „User ${user.name} has role ${user.role}” moeten schrijven — herhaaldelijk user herhalen. In het tweede voorbeeld groepeert with de configuratie van TextView: alle methoden en eigenschappen worden op één object aangeroepen zonder herhaling. Nadeel van with — onmogelijkheid om in een keten te gebruiken, omdat het geen extensie is en het contextobject niet retourneert.
Veelgestelde vragen
Gebruik apply — het retourneert het object en biedt toegang via this, waardoor eigenschappen kunnen worden ingesteld zonder herhaling van de objectnaam. also is geschikt als een expliciete parameter it nodig is voor onderscheid van de eigenschappen van het object.
let geeft context door via it (expliciete parameter), run — via this (impliciete ontvanger). let is handig voor null-controles en transformaties, run — voor berekeningen met context. Beide retourneren het resultaat van de lambda.
with is geen extensie — het neemt het object als argument. Gebruik with voor het groeperen van aanroepen wanneer een keten niet nodig is. apply — extensie die het object retourneert, handig in initialisatoren en builder-patronen.
Ja, maar wees voorzichtig met this — geneste this veroorzaken verwarring. Gebruik let of also met expliciete namen voor geneste contexten om dubbelzinnigheid bij het verwijzen naar het externe object te voorkomen.
Alle scope functions zijn inline, dus ze creëren geen overhead voor anonieme klassen. Het prestatieverschil tussen hen is verwaarloosbaar. De keuze moet gebaseerd zijn op leesbaarheid en conventies, niet op snelheid.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook