Extensiefuncties zijn een van de belangrijkste verschillen tussen Kotlin en Java, die voor het eerst verschenen in de vroege versies van de taal, geïnspireerd door C#. Ze stellen je in staat om nieuwe methoden toe te voegen aan bestaande klassen zonder loten of modificatie van de oorspronkelijke klasse. Dit verhoogt de expressiviteit en beknoptheid van de code aanzienlijk.
In Java en vele andere talen is het onmogelijk om methoden toe te voegen aan een al bestaande klasse (bijvoorbeeld, String of List) zonder overerving of het Decorator-patroon te gebruiken. Kotlin lost dit gebrek op met behulp van het mechanisme van extensiefuncties.
Veel klassen in de bibliotheek (bijvoorbeeld, String, Int, List) kunnen niet direct worden gewijzigd. Vaak zijn er extra methoden nodig om de code leesbaarder te maken en duplicatie van logica te vermijden. Extensiefuncties wijzigen echter de werkelijke klasse niet — ze worden statisch gecreëerd en hebben binnenin geen toegang tot private/protected leden.
In Kotlin ziet de declaratie van een extensiefunctie er als volgt uit:
fun String.lastChar(): Char = this[this.length - 1]
Nu kan deze methode voor elke String worden aangeroepen:
"Kotlin".lastChar() // geeft 'n' terug
Kan een extensiefunctie worden overschreven in de afgeleide klasse?
Nee. Extensiefuncties zijn geen leden van de klasse, ze kunnen niet worden overschreven met 'override': hun statisch karakter blijkt zelfs voor afgeleiden klassen — de compiler kiest de functie op basis van het type van de expressie, niet op basis van het werkelijke type van het object.
open class Base class Derived : Base() fun Base.foo() = "base" fun Derived.foo() = "derived" fun printFoo(b: Base) { println(b.foo()) } val d = Derived() printFoo(d) // geeft "base" weer
Kunnen extensiefuncties de toestand van een object wijzigen?
Extensiefuncties kunnen geen toegang krijgen tot private/protected toestand, maar als het type mutabel is (bijvoorbeeld, List of een eigen klasse), kunnen ze de beschikbare externe toestand wijzigen via publieke methoden.
fun MutableList<Int>.addTwice(elem: Int) { add(elem) add(elem) }
Conflicteren extensiefuncties met leden van de klasse met dezelfde naam?
Als de naam van de extensie overeenkomt met de naam van een echte methode, blijft de prioriteit bij het lid van de klasse. De extensie wordt alleen aangeroepen als de leden ontbreken of niet zichtbaar zijn.
class Foo { fun bar() = "lid" } fun Foo.bar() = "extensie" val f = Foo() println(f.bar()) // "lid"
Een ontwikkelaar breidt de standaard String uit met een functie die een project-specifiek formaat instelt en gebruikt deze overal — andere ontwikkelaars merken niet op dat dit geen standaardoperatie van de String-klasse is.
Voordelen:
Nadelen:
Uitbreiding van de standaardklasse met een utility-methode die precies is gelokaliseerd in het pakket utils en goed is gedocumenteerd. Het gebruik is beperkt tot expliciet afgesproken delen van de code.
Voordelen:
Nadelen: