extension function — 継承なしで、ソースコードを変更せずに既存のクラスに新しいメソッドを追加できるKotlinのメカニズム。関数はレシーバータイプのプレフィックス付きで宣言され、そのタイプの通常のメソッドとして呼び出されます。Kotlin Documentation, 2025によると、extension functionsはレシーバーを最初のパラメーターとして持つ静的メソッドにコンパイルされ、通常の呼び出しと比較して実行時にゼロオーバーヘッドを保証します。
主要ポイント
Extension functionは、クラスの外部で宣言されながら、そのメソッドとして呼び出される関数です。新しいメソッドが追加されるレシーバータイプを受け取ります。関数内では、キーワードthisがレシーバータイプのインスタンスを参照します。このアプローチにより、finalクラス、サードパーティライブラリのクラス、さらにはプリミティブ型まで、ラッパーを作成せずに拡張できます。
Javaでは既存のクラスにメソッドを追加するために継承か静的ユーティリティクラスが必要ですが、Kotlinはボイラープレートなしでエレガントなソリューションを提供します。Kotlin Foundationの調査(2024年)によると、extension functionsは言語で最も使用される機能トップ5に入っており、Kotlin開発者の78%が日常業務で使用しています。
Kotlinコンパイラはextension functionを静的メソッドに変換し、レシーバーインスタンスが最初の引数として渡されます。つまり、extension functionsはターゲットクラスのバイトコードを変更せず、カプセル化を破りません — レシーバーのパブリックフィールドとメソッドのみがアクセス可能です。
特定のタイプに論理的に関連付けられているが、そのソースコードに追加できない、クリーンなユーティリティ関数を記述するにはextension functionsを使用してください。
Extension functionの基本構文: レシーバータイプ名、ドット、関数名、次にパラメーターと戻り値の型。関数内では、レシーバーはthisを介してアクセスできます。
// Extension function for String
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Called as regular String method
val result = "user@example.com".isEmail() // true
この例では、isEmail()がすべての文字列で使用可能になります。関数内では、thisはメソッドが呼び出された文字列自体を参照します。Kotlinはほとんどの場合、thisを省略できます — this.contains("@")の代わりに単にcontains("@")と書けます。
Extension functionsは追加のパラメーターを受け入れ、任意の型の値を返せます。これにより、単なる構文糖衣ではなく、本格的な関数になります。
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)
defaultパラメーターは空のリストに対して返される値を定義します。Extension functionはジェネリックにできます — List
Kotlinのextension functionsは静的ディスパッチを使用し、仮想ディスパッチは使用しません。これは通常のクラスメソッドとの重要な違いです。どのextension functionが呼ばれるかは、実行時の実際の型ではなく、変数の静的型によってコンパイル時に決定されます。
open class Animal
class Dog : Animal()
fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"
fun test() {
val animal: Animal = Dog()
println(animal.speak()) // "Animal sound" — static type Animal
val dog: Dog = Dog()
println(dog.speak()) // "Woof" — static type Dog
}
animal変数がDogのインスタンスを指している場合でも、変数の静的型がAnimalであるため、Animalのextension functionが呼び出されます。speak()が仮想クラスメソッドであれば、Dog.speak()が呼び出されていたでしょう。この動作は、extension functionsでAPIを設計する際に考慮することが重要です。
Extension functionsはクラスメンバーではありません — 別のファイルで使用するにはインポートが必要な通常の関数です。Kotlinは2つのインポートオプションを提供します: 関数名によるインポートと、名前変更を伴うインポートです。
// Import by name
import com.example.extensions.isEmail
// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail
// Call after import
val valid = "test@test.com".isEmail()
asによる名前変更は名前の競合に役立ちます — たとえば、2つのライブラリが同じ型に対して同じ名前のextension functionを提供する場合です。この場合、一方を別の名前でインポートし、新しい名前を使用して呼び出すことができます。
Extension functionsはさまざまなレベルで宣言できます:
| レベル | 可視性 | 例 |
|---|---|---|
| Top-level | インポート後、プロジェクト全体 | fun String.isEmail() |
| Member extension | 所有者クラス内 | class A { fun B.ext() } |
| Local | 関数内 | fun test() { fun String.ext() } |
Member extension functionsは、extension functionが別のクラス内で宣言される特殊なケースです。この場合、extension function内ではレシーバー(関数のthis)と外部クラスのメンバーの両方にアクセスできます。
クラスにextension functionと同じシグネチャのメソッドがある場合、常にクラスメンバーが呼び出されます。Extension functionはクラスメソッドを決してオーバーライドしません — これは偶発的なオーバーライドを防ぐためのアーキテクチャ上の決定です。
class User {
fun greet() = "Hello from class"
}
fun User.greet() = "Hello from extension"
fun main() {
val user = User()
println(user.greet()) // "Hello from class"
}
extension functionが後で定義され、同じシグネチャを持つ場合でも、コンパイラはクラスメソッドを選択します。Kotlinコンパイラはこの状況を検出すると警告を発します。クラスメソッドが存在する場合にextension functionを呼び出す唯一の方法は、通常の関数として呼び出すことです: greet(user)。
Android開発では、extension functionsはView、Context、フラグメントを扱うための標準ツールになっています。Android KTXライブラリはextension functions上に構築されており、Android APIに対する便利なラッパーを提供しています。
// Extension function for working with View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
// Extension function for Context — toast
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
// Extension function as DSL builder
fun String.colorFormat(color: String): String =
"$color$this\u001b[0m"
toast() extension functionはContext依存の呼び出しを簡潔にします: Toast.makeText(context, message, length).show()と書く代わりに、context.toast("テキスト")と書くだけです。これによりボイラープレートが減り、コードがより読みやすくなります。
よくある質問
いいえ、extension functionsは静的ディスパッチを使用します。基底クラスとサブクラスに同じextension functionを宣言した場合、どちらが呼び出されるかは、実行時の実際の型ではなく、コンパイル時の変数の静的型によって決定されます。
null許容レシーバータイプに対してextension functionsを宣言できます: fun String?.isNullOrEmail()。このような関数内では、thisがnullになる可能性があるため、安全呼び出し演算子または明示的なnullチェックを使用する必要があります。
いいえ、extension functionsは静的メソッドにコンパイルされます。バイトコードレベルでは、extension functionの呼び出しはレシーバーを最初のパラメーターとする静的メソッドの呼び出しと同じです。リフレクションや動的ディスパッチは一切含まれません。
はい、extension propertiesはextension functionsと同様に動作しますが、状態を保持できません — getterとsetterのみです。例: val List
JavaではStringUtils.isEmail(str)と書きますが、Kotlinではstr.isEmail()と書きます。違いは構文だけではありません: extension functionsはIDEの自動補完をサポートし、呼び出しチェーンの可読性を向上させ、IDEが特定の型に関連する関数を提案できるようにします。Javaにはこれらの機能はありません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。