rozšiřující funkce — mechanismus Kotlin, který umožňuje přidávat nové metody k existujícím třídám bez dědičnosti a bez změny zdrojového kódu. Funkce je deklarována s prefixem ve formě typu-příjemce a volána jako běžná metoda tohoto typu. Podle Kotlin Documentation, 2025 jsou rozšiřující funkce kompilovány do statických metod s prvním parametrem-příjemcem, což zaručuje nulovou režii za běhu ve srovnání s běžnými voláními.
Hlavní body
Rozšiřující funkce je funkce deklarovaná mimo třídu, ale volaná jako její metoda. Přijímá typ-příjemce, ke kterému je přidána nová metoda. Uvnitř funkce klíčové slovo this odkazuje na instanci typu příjemce. Tento přístup umožňuje rozšíření finálních tříd, tříd knihoven třetích stran a dokonce primitivních typů bez vytváření obálek.
Na rozdíl od Javy, kde přidání metody k existující třídě vyžaduje buď dědičnost, nebo statickou pomocnou třídu, Kotlin nabízí elegantní řešení bez boilerplate. Podle údajů výzkumu Kotlin Foundation (2024) patří rozšiřující funkce mezi pět nejčastěji používaných funkcí jazyka — 78 % Kotlin vývojářů je používá v každodenní práci.
Kompilátor Kotlin převádí rozšiřující funkci na statickou metodu, kde je instance příjemce předána jako první argument. To znamená, že rozšiřující funkce nemění bajtkód cílové třídy a nenarušují zapouzdření — jsou přístupná pouze veřejná pole a metody příjemce.
Používejte rozšiřující funkce pro psaní čistých pomocných funkcí, které jsou logicky vázány na určitý typ, ale nelze je přidat do jeho zdrojového kódu.
Základní syntaxe rozšiřující funkce: název typu-příjemce, tečka, název funkce, poté parametry a návratový typ. Uvnitř funkce je příjemce přístupný přes this.
// Rozšiřující funkce pro String
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Volána jako běžná metoda String
val result = "user@example.com".isEmail() // true
V tomto příkladu se isEmail() stává dostupným pro všechny řetězce. Uvnitř funkce je this samotný řetězec, na kterém je metoda volána. Kotlin umožňuje vynechat this ve většině případů — lze jednoduše napsat contains("@") místo this.contains("@").
Rozšiřující funkce mohou přijímat další parametry a vracet hodnoty libovolného typu. To z nich dělá plnohodnotné funkce, nejen syntaktický cukr.
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
if (this.isEmpty()) return listOf(default)
return this
}
val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)
Parametr default určuje hodnotu vrácenou pro prázdný seznam. Rozšiřující funkce může být generická — typ List
Rozšiřující funkce v Kotlin používají statickou dispečink, nikoli virtuální. To je klíčový rozdíl od běžných metod tříd. Která rozšiřující funkce bude volána, se určuje ve fázi kompilace podle statického typu proměnné, nikoli podle jejího skutečného typu za běhu.
open class Animal
class Dog : Animal()
fun Animal.speak() = "Zvuk zvířete"
fun Dog.speak() = "Štěkot"
fun test() {
val animal: Animal = Dog()
println(animal.speak()) // „Zvuk zvířete" — statický typ Animal
val dog: Dog = Dog()
println(dog.speak()) // „Štěkot" — statický typ Dog
}
Přestože proměnná animal ukazuje na instanci Dog, volá se rozšiřující funkce pro Animal, protože statický typ proměnné je Animal. Pokud by speak() byla virtuální metoda třídy, volalo by se Dog.speak(). Tato vlastnost je důležitá při navrhování API založeného na rozšiřujících funkcích.
Rozšiřující funkce nejsou členy třídy — jsou to běžné funkce, které vyžadují import pro použití v jiném souboru. Kotlin nabízí dvě varianty importu: podle názvu funkce nebo s přejmenováním.
// Import podle názvu
import com.example.extensions.isEmail
// Import s aliasem (pro řešení konfliktů)
import com.example.extensions.isEmail as isValidEmail
// Volání po importu
val valid = "test@test.com".isEmail()
Přejmenování pomocí as je užitečné při konfliktech názvů — například pokud dvě knihovny poskytují rozšiřující funkci se stejným názvem pro stejný typ. V tomto případě lze jednu z nich importovat pod jiným názvem a volat přes nový název.
Rozšiřující funkce lze deklarovat na různých úrovních:
| Úroveň | Viditelnost | Příklad |
|---|---|---|
| Top-level | Celý projekt po importu | fun String.isEmail() |
| Member extension | Uvnitř třídy vlastníka | class A { fun B.ext() } |
| Local | Uvnitř funkce | fun test() { fun String.ext() } |
Member extension functions — zvláštní případ, kdy je rozšiřující funkce deklarována uvnitř jiné třídy. V tomto případě jsou uvnitř rozšiřující funkce přístupní jak příjemce (this funkce), tak členové vnější třídy.
Pokud má třída metodu se stejným podpisem jako rozšiřující funkce, vždy se volá člen třídy. Rozšiřující funkce nikdy nepřepisuje metodu třídy — to je architektonické rozhodnutí pro zabránění náhodným přepsáním.
class User {
fun greet() = "Ahoj z třídy"
}
fun User.greet() = "Hello from extension"
fun main() {
val user = User()
println(user.greet()) // „Ahoj z třídy"
}
I když je rozšiřující funkce definována později a má stejný podpis, kompilátor vybere metodu třídy. Kompilátor Kotlin vydá varování při detekci takové situace. Jediný způsob, jak zavolat rozšiřující funkci při existenci metody třídy, je obrátit se na ni jako na běžnou funkci: greet(user).
Ve vývoji pro Android se rozšiřující funkce staly standardním nástrojem pro práci s View, Context a fragmenty. Knihovna Android KTX je postavena na rozšiřujících funkcích a poskytuje pohodlné obálky pro Android API.
// Rozšiřující funkce pro práci s View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
// Rozšiřující funkce pro Context — toast
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
// Rozšiřující funkce jako stavitel DSL
fun String.colorFormat(color: String): String =
"$color$this\u001b[0m"
Rozšiřující funkce toast() činí volání závislá na Context stručnými: místo psaní Toast.makeText(context, message, length).show() stačí napsat context.toast("Text"). To snižuje boilerplate a činí kód čitelnějším.
Často kladené otázky
Ne, rozšiřující funkce používají statickou dispečink. Pokud deklarujete rozšiřující funkci pro základní třídu a stejnou pro podtřídu, která z nich bude volána, určuje statický typ proměnné ve fázi kompilace, nikoli skutečný typ za běhu.
Lze deklarovat rozšiřující funkce pro nullable typy příjemce: fun String?.isNullOrEmail(). Uvnitř takové funkce může být this null, proto je nutné použít operátory bezpečného volání nebo explicitní kontrolu null.
Ne, rozšiřující funkce jsou kompilovány do statických metod. Na úrovni bajtkódu je volání rozšiřující funkce totožné s voláním statické metody s prvním parametrem-příjemcem. Žádná reflexe nebo dynamická dispečink.
Ano, rozšiřující vlastnosti fungují podobně jako rozšiřující funkce, ale nemohou ukládat stav — pouze getter a setter. Například: val List<Int>.sum get() = reduce(Int::plus). Jsou také kompilovány do statických getter/setter metod.
V Javě píšete StringUtils.isEmail(str), v Kotlin — str.isEmail(). Rozdíl není jen v syntaxi: rozšiřující funkce podporují automatické doplňování v IDE, zlepšují čitelnost řetězců volání a umožňují IDE navrhovat relevantní funkce pro konkrétní typ. V Javě takové možnosti neexistují.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také