言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. 拡張関数

拡張関数

対応: Kotlin 1.0(2016)

Kotlinの「拡張関数」は、既存のクラスを継承せずにメソッドを追加できる機能です。レシーバー型(拡張する型)を指定した通常の関数として定義し、そのクラスのメソッドのように呼び出せます。

構文

// 拡張関数の定義(String を拡張する例)
fun String.hello(): String = "Hello, $this!"

// 呼び出し
"Kotlin".hello() // "Hello, Kotlin!"

// 引数ありの拡張関数
fun String.repeat(n: Int): String = this.repeat(n)

// 拡張プロパティ(getterのみ定義可能)
val String.wordCount: Int
    get() = this.split(" ").size

// Nullable型への拡張関数
fun String?.isNullOrBlankCustom(): Boolean = this == null || this.isBlank()

構文一覧

構文概要
fun 型名.関数名()拡張関数の定義です。「型名.」がレシーバー型の指定です。
this拡張関数内でレシーバーオブジェクト(呼び出し元)を指します。
val 型名.プロパティ名拡張プロパティの定義です。状態は持てず getter のみ定義できます。
Nullable 拡張関数型名を「型?」にすると null 値に対しても呼び出せます。

サンプルコード

extension_function.kt
// String の拡張関数
fun String.isPalindrome(): Boolean = this == this.reversed()
fun String.capitalize2(): String = if (isEmpty()) this else this[0].uppercaseChar() + substring(1)
fun String.words(): List<String> = this.trim().split(Regex("\\s+"))

// Int の拡張関数
fun Int.isPositive(): Boolean = this > 0
fun Int.factorial(): Long {
    require(this >= 0) { "負の数の階乗は未定義です" }
    return if (this <= 1) 1L else (1..this).fold(1L) { acc, i -> acc * i }
}

// 拡張プロパティ
val String.wordCount: Int
    get() = if (isBlank()) 0 else words().size

val Int.isOdd: Boolean
    get() = this % 2 != 0

// List の拡張関数
fun <T> List<T>.secondOrNull(): T? = if (size >= 2) this[1] else null

fun main() {
    // String の拡張関数
    println("racecar".isPalindrome()) // true
    println("hello".isPalindrome()) // false
    println("hello world".capitalize2()) // Hello world

    val sentence = "  Hello   Kotlin   World  "
    println(sentence.words()) // [Hello, Kotlin, World]
    println(sentence.wordCount) // 3

    // Int の拡張関数
    println(5.isPositive()) // true
    println((-3).isPositive()) // false
    println(5.factorial()) // 120
    println(7.isOdd) // true

    // List の拡張関数
    val list = listOf("a", "b", "c")
    println(list.secondOrNull()) // b
    println(emptyList<String>().secondOrNull()) // null

    // 標準ライブラリのような使い方
    val numbers = listOf(1, 2, 3, 4, 5)
    println(numbers.filter { it.isOdd }) // [1, 3, 5]
}

コンパイルして実行すると次のようになります。

kotlinc extension_function.kt -include-runtime -d extension_function.jar
java -jar extension_function.jar
true
false
Hello world
[Hello, Kotlin, World]
3
true
false
120
true
b
null
[1, 3, 5]

よくあるミス

よくあるミス1: メンバー関数と同名の拡張関数を定義しても、メンバー関数が優先される。

NG例1 — 同名メンバー関数があると拡張関数は無視される
class Member(val name: String) {
    fun greet() = "名前は ${name}"
}

fun Member.greet() = "拡張関数の greet" // メンバー関数があるため呼ばれない

fun main() {
    val m = Member("item_x")
    println(m.greet()) // 名前は item_x(拡張関数は無視される)
}

拡張関数はメンバー関数を上書きできない。別の名前を付けるか、メンバー関数自体を変更する必要がある。

よくあるミス2: 拡張プロパティにバッキングフィールドを定義しようとするとコンパイルエラーになる。

NG例2 — 拡張プロパティに初期化子を書く
// val String.cachedLength: Int = this.length // コンパイルエラー
// 拡張プロパティは状態を持てないため、初期化子を書けない
val String.cachedLength: Int = this.length
extension_function_ok2.kt
val String.wordCount: Int
    get() = if (isBlank()) 0 else trim().split(Regex("\\s+")).size

fun main() {
    println("sample data test".wordCount) // 3
}

コンパイルして実行すると次のようになります。

kotlinc extension_function_ok2.kt -include-runtime -d extension_function_ok2.jar
java -jar extension_function_ok2.jar
3

よくあるミス3: non-nullable なレシーバーへの拡張関数に『null』を渡すとコンパイルエラーになる。

NG例3 — null を渡せない拡張関数を定義する
fun String.upperFirst(): String = this[0].uppercaseChar() + substring(1)

fun main() {
    val name: String? = null
    // println(name.upperFirst()) // コンパイルエラー: String? を String に渡せない
}
extension_function_ok3.kt
fun String?.safeUpperFirst(): String = this?.let {
    if (it.isEmpty()) it else it[0].uppercaseChar() + it.substring(1)
} ?: ""

fun main() {
    val name: String? = null
    println(name.safeUpperFirst()) // ""(例外なし)
    println("sample".safeUpperFirst()) // Sample
}

コンパイルして実行すると次のようになります。

kotlinc extension_function_ok3.kt -include-runtime -d extension_function_ok3.jar
java -jar extension_function_ok3.jar

Sample

概要

拡張関数はJavaのユーティリティクラス(例: StringUtils.isNotEmpty())をメソッドのような構文で呼べるようにする仕組みです。コンパイル後は静的関数として展開されるため、実際にクラスを変更したりオーバーライドしたりはしません。

ジェネリクスを使った拡張関数『fun <T> List<T>.xxx()』は型を抽象化した汎用メソッドを追加できます。Kotlinの標準ライブラリ(『map()』『filter()』など)もほとんどが拡張関数として実装されています。

基本的な関数定義はfun 関数の定義を、高階関数については高階関数を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。