KotlinのExtension function:概要、構文、応用

著者: IT Sectr 公開日: 2026-06-21 読了時間: 9 分

extension function — 継承なしで、ソースコードを変更せずに既存のクラスに新しいメソッドを追加できるKotlinのメカニズム。関数はレシーバータイプのプレフィックス付きで宣言され、そのタイプの通常のメソッドとして呼び出されます。Kotlin Documentation, 2025によると、extension functionsはレシーバーを最初のパラメーターとして持つ静的メソッドにコンパイルされ、通常の呼び出しと比較して実行時にゼロオーバーヘッドを保証します。

主要ポイント

  • Extension function — レシーバータイプを持つメソッドで、継承なしでクラスメンバーのように呼び出せる
  • レシーバータイプは関数名の前にドットで指定: fun Type.method()
  • 静的ディスパッチ — extension functionは仮想ではなく、変数のタイプに基づいて呼び出される
  • インポート必須: extension functionsはクラスメンバーではなく、インポートが必要
  • クラスメンバーが優先: メソッドが一致する場合、拡張ではなくクラスメンバーが呼び出される

Kotlinのextension functionとは?

Extension functionは、クラスの外部で宣言されながら、そのメソッドとして呼び出される関数です。新しいメソッドが追加されるレシーバータイプを受け取ります。関数内では、キーワードthisがレシーバータイプのインスタンスを参照します。このアプローチにより、finalクラス、サードパーティライブラリのクラス、さらにはプリミティブ型まで、ラッパーを作成せずに拡張できます。

Javaでは既存のクラスにメソッドを追加するために継承か静的ユーティリティクラスが必要ですが、Kotlinはボイラープレートなしでエレガントなソリューションを提供します。Kotlin Foundationの調査(2024年)によると、extension functionsは言語で最も使用される機能トップ5に入っており、Kotlin開発者の78%が日常業務で使用しています。

Kotlinコンパイラはextension functionを静的メソッドに変換し、レシーバーインスタンスが最初の引数として渡されます。つまり、extension functionsはターゲットクラスのバイトコードを変更せず、カプセル化を破りません — レシーバーのパブリックフィールドとメソッドのみがアクセス可能です。

特定のタイプに論理的に関連付けられているが、そのソースコードに追加できない、クリーンなユーティリティ関数を記述するにはextension functionsを使用してください。

Extension functionの構文: 宣言と呼び出し

Extension functionの基本構文: レシーバータイプ名、ドット、関数名、次にパラメーターと戻り値の型。関数内では、レシーバーはthisを介してアクセスできます。

kotlin
// 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 function

Extension functionsは追加のパラメーターを受け入れ、任意の型の値を返せます。これにより、単なる構文糖衣ではなく、本格的な関数になります。

kotlin
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型はジェネリックですが、例では特定のListを使用しています。

静的ディスパッチとレシーバー

Kotlinのextension functionsは静的ディスパッチを使用し、仮想ディスパッチは使用しません。これは通常のクラスメソッドとの重要な違いです。どのextension functionが呼ばれるかは、実行時の実際の型ではなく、変数の静的型によってコンパイル時に決定されます。

kotlin
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のインポートとスコープ

Extension functionsはクラスメンバーではありません — 別のファイルで使用するにはインポートが必要な通常の関数です。Kotlinは2つのインポートオプションを提供します: 関数名によるインポートと、名前変更を伴うインポートです。

kotlin
// 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はクラスメソッドを決してオーバーライドしません — これは偶発的なオーバーライドを防ぐためのアーキテクチャ上の決定です。

kotlin
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)。

Extension functionsの実践例

Android開発では、extension functionsはView、Context、フラグメントを扱うための標準ツールになっています。Android KTXライブラリはextension functions上に構築されており、Android APIに対する便利なラッパーを提供しています。

kotlin
// 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 functionをオーバーライドできますか?

いいえ、extension functionsは静的ディスパッチを使用します。基底クラスとサブクラスに同じextension functionを宣言した場合、どちらが呼び出されるかは、実行時の実際の型ではなく、コンパイル時の変数の静的型によって決定されます。

Extension functionsはnull許容型でどのように動作しますか?

null許容レシーバータイプに対してextension functionsを宣言できます: fun String?.isNullOrEmail()。このような関数内では、thisがnullになる可能性があるため、安全呼び出し演算子または明示的なnullチェックを使用する必要があります。

Extension functionsにパフォーマンスのオーバーヘッドはありますか?

いいえ、extension functionsは静的メソッドにコンパイルされます。バイトコードレベルでは、extension functionの呼び出しはレシーバーを最初のパラメーターとする静的メソッドの呼び出しと同じです。リフレクションや動的ディスパッチは一切含まれません。

クラスにextension propertyを宣言できますか?

はい、extension propertiesはextension functionsと同様に動作しますが、状態を保持できません — getterとsetterのみです。例: val List.sum get() = reduce(Int::plus)。これらも静的getter/setterメソッドにコンパイルされます。

Extension functionsはJavaのユーティリティクラスのメソッドとどう違いますか?

JavaではStringUtils.isEmail(str)と書きますが、Kotlinではstr.isEmail()と書きます。違いは構文だけではありません: extension functionsはIDEの自動補完をサポートし、呼び出しチェーンの可読性を向上させ、IDEが特定の型に関連する関数を提案できるようにします。Javaにはこれらの機能はありません。

まとめ

  • Extension function — レシーバータイプを持つメソッドで、継承なしでクラスメンバーのように呼び出せる
  • 静的ディスパッチ — 実行時タイプではなく静的タイプによる関数選択
  • ゼロオーバーヘッド — レシーバーを最初のパラメーターとする静的メソッドにコンパイル
  • インポートが他のファイルでの使用に必須、asによるエイリアスをサポート
  • クラスメンバーが同じシグネチャのextension functionsより優先
  • Member extensions — クラス内で宣言された拡張はそのメンバーにアクセス可能
  • 実用的な使用 — String、View、Context、コレクション、DSL構築のためのユーティリティ

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください