夢と魔法の待ち時間というAndroidアプリを趣味で作っていて、これを100% Kotlinに移行するためここ最近ごりごりコードを書います。
1つのフラグメントで両パーク(陸と海)に対応するようにしていて、そんな感じのフラグメントがいくつかあります。
例えばアトラクション一覧のフラグメントとか、グリーティング一覧のフラグメントとか。
どちらのパークかはarguments
で与えられるという寸法です。
class AttractionFragment: Fragment() { val park: Park get() = arguments.getSerializable("park") as Park // 省略 }
class GreetingFragment: Fragment() { val park: Park get() = arguments.getSerializable("park") as Park // 省略 }
だいたいこんな感じでpark
の部分が完全に一致。
すっきり記述できる方法はないかなーと考えました(BaseFragment
とかの導入は却下)。
ScalaのSelf Typeみたいなやつ (失敗)
失敗というか既に廃止された言語機能です。
次のコードのように、インタフェースが自分の実装されるクラスを指定できる感じです。
WithPark
インタフェースはFragment
を要求しているので、自分の中でFragment
のarguments
にアクセスできます。
interface WithPark: Fragment { val park: Park get() = arguments.getSerializable("park") as Park } class AttractionFragment: Fragment(), WithPark { fun とあるメソッド() { println(park) // パークをargumentsから取れる } }
インタフェースだけで頑張る (成功)
話の順序的に真ん中に成功例を載せます。
arguments
を抽象プロパティとして提供するインタフェースにすれば、それっぽくなります。
interface WithPark { fun getArguments(): Bundle val park: Park get() = getArguments().getSerializable("park") as Park } class AttractionFragment: Fragment(), WithPark { fun とあるメソッド() { println(park) // パークをargumentsから取れる } }
問題があるとすればFragment
以外がWithPark
になれる点です。
いや、こっちの方がより抽象的でいいのかな。でもスコープというか使用できる範囲は狭いほうがいいような。。
拡張プロパティで頑張る (失敗)
最後にまた失敗例です。
今回はWithPark
インタフェースは、拡張プロパティのための印です。
interface WithPark val <T> T.park: String where T: Fragment, T: WithPark get() = argument.getString("park") as Park class AttractionFragment: Fragment(), WithPark { fun とあるメソッド() { println(park) // なぜかここでコンパイルエラー } }
拡張プロパティpark
は、Fragment
かつWithPark
な型をレシーバに取ります。
これにより最初に挙げた例のSelf Typeのようなことができます。
WithPark
は誰でも実装できますが、それがFragment
以外だとpark
プロパティを呼び出せません。
しかし何故か呼び出し箇所でコンパイルエラーになってしまいます。 数日前に同じ問題に直面した人がチケット上げてました。
https://youtrack.jetbrains.com/issue/KT-9630
(僕の中での)結論
3番目の方法ができるのを待って、それまでは2番目の方法で凌ぐ。