LoginSignup
1
0

More than 3 years have passed since last update.

Kotlinの拡張関数でハマったこと

Last updated at Posted at 2019-09-03

拡張 - Kotlin Programming Language
リファレンスにある拡張関数について、ハマったことがあったので、調べてみました。

追加したNull許容レシーバーの処理が実行されない

例えば以下のソースコードがあったとします。

class C {
    fun foo() { println("member") }
}

// 今回実行したいNull許容レシーバー
fun C?.toSring(): String { return "extension" }

fun main() {
    var c: C? = null

    println(c.toString()) // extensionではなくnullが出力されてしまう
}

Null許容型と非null許容型は明確に区別される

リファレンスによると、

拡張は、null許容なレシーバの型で定義できることに注意してください。このような拡張は、その値がnullの場合でも、オブジェクト変数で呼び出すことができ、かつその本体内で this == null をチェックすることができます。

だそうです。例えば以下のコードのように、

class C {
    fun foo() { println("member") }
}

// 今回実行したいNull許容レシーバー
fun C?.toSring(): String { return "extension" }

// CクラスにあるメソッドをNull許容レシーバーとして追加
fun C?.foo() { println("aaaaaa") }

fun main() {
    var c: C? = null

    println(c.toString()) // extensionではなくnullが出力されてしまう
    c.foo() // aaaaaa

    var c2 = C()
    c2.foo() // member
}

cはnullなので、Null許容レシーバーとして追加した方が呼ばれ、c2はメンバメソッドが呼ばれます。

この理論でいけば、toString()なんかも同様の結果になるはずですが、実際はそうはなりませんでした。

toString()はAnyクラスに存在する関数だから?

Kotlinにおいて、全てのクラスはAnyクラスの子クラスであります。
Any - Kotlin Programming Language

Anyクラスは、toString()や、hashCode()などのメンバメソッドを持っています。
また、拡張関数の特徴として、

もし、あるクラスがメンバ関数を持つうえ、さらに、同じレシーバ型、同じ名前を有し、与えられた引数を受容可能な拡張関数が宣言されると、 常にメンバが優先されます。

があります。
そのため、拡張関数toString()を追加したとしても、すでにAnyクラスに定義済みのtoString()が呼ばれてしまっているため、反映されないのではと考えます。

拡張関数の説明に関しては、以下のスライドがわかりやすいと感じました。
みんな大好き拡張関数 #kotlin_sansan - Speaker Deck

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0