三行で
"abcd".ex.doubleStr() // abcdabcd
Optional.of(3).ex.unwrap // 3
拡張関数、拡張パラメータの呼び出しが上のようにex.
を挟むことができます。
これはつまり?
Kotlinの拡張関数、拡張プロパティ便利だけど、標準やライブラリの拡張関数、拡張プロパティとは区別ができるようにしたいので、呼び出す前にex.
を付けられるようにしてみた。と言う話です。
きっかけ
最近Swift界隈では拡張関数や拡張パラメータにアクセスする時に、Rxの拡張関数だとrx.
、自前の拡張関数だとex.
みたいなものが流行っている(?)みたい話を耳にしたので、Kotlinでも同じようにしたい!!と思ったので作りました。
例えばこんな感じのやつです
https://qiita.com/tattn/items/dc7dfe2fceec00bb4ff7#exでアクセスできるプロパティやメソッドを作る
実装
Swiftのコードを見ると難しそうに見えますが、やってみると意外に簡単でした。
実際のコードです。
data class TargetedExtension<out Base>(val base: Base)
val <Compatible> Compatible.ex: TargetedExtension<Compatible>
get() = TargetedExtension(this)
これだけです。めっちゃ簡単ですね。
Sample Code
早速これを使って、どう拡張関数、拡張パラメータを定義する必要があるのか見てください。
// 拡張関数定義
fun TargetedExtension<String>.doubleStr(): String = base + base
fun TargetedExtension<Int>.square(): Int = base * base
// 拡張パラメータ定義
val TargetedExtension<String>.doubleStrValue: String
get() = base + base
val TargetedExtension<Optional<Int>>.unwrap: Int
get() = base.get()
val TargetedExtension<Number>.half
get() = base.toDouble() / 2
"test".ex.doubleStr() // testtest
12.ex.square() // 144
"abc".ex.doubleStrValue // abcabc
Optional.of(3).ex.unwrap // 3
3.55.ex.half //1.775
(思ったように動いて安心しました)
コードの説明
以下拡張関数、拡張パラメータの説明が重複するので、拡張パラメータを使ったパターンについての説明をします。
拡張関数の説明も拡張パラメータと同じです。
3.55.ex.half
を分解すると左から
- 3.55: 本来拡張変数を持たせたい型のインスタンス(レシーバ)
- ex: 型パラメータを使って定義された
ex
拡張パラメータ(関節レシーバ) - half: 関節レシーバから呼び出された拡張関数((関節)レシーバの拡張パラメータ)
本来は1
と3
の部分だけで構成して関節部分を挟まない定義が当たり前ですが、3
の部分の定義を2
のex
が返すインスタンスの型の拡張パラメータとして定義することで、間にex
を挟めるようにしたと言うことです。
3.55(本レシーバ).ex(関節レシーバ).half(関節レシーバに定義した拡張パラメータ)
- 本レシーバのインスタンスを
TargetedExtension
でラップしたインスタンスをex
からもらえるようにする。 -
TargetedExtension
の型パラメータを指定した拡張関数を定義する。
の複合要素の合体技という感じで、ちょっと直感的じゃないのが難点かなという気がしますが、慣れたら問題はないかなぁという気がしています。型パラメータをつなぐ使い方がテクニカルなポイントでしょうか。
- ライブラリやなんかの拡張関数、拡張パラメータとは呼び方を変えたい
- 自分のライブラリの拡張関数、拡張パラメータは、特別なアクセスのさせ方をしたい
- 自作のものが他と被るのが嫌だったから拡張関数、拡張パラメータの利用を避けていたけど、それがこれなら問題がクリアされる
みたいに刺さればいいなと思っています。
以上。