Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaからKotlinへの移行(相互運用ハマりポイント) #ca_kt

JavaからKotlinへの移行(相互運用ハマりポイント) #ca_kt

CA.kt #1 で発表したスライドです。
https://cyberagent.connpass.com/event/57963/

Taro Nagasawa

June 15, 2017
Tweet

More Decks by Taro Nagasawa

Other Decks in Programming

Transcript

  1. JUnit • Javaのテスティングフレームワーク • Kotlinからも使えます class CalculatorTest { private val

    sut = Calculator() @Test fun `1 + 2 = 3となること`() { val got = sut.plus(1, 2) assertThat(got).isEqualTo(3) } }
  2. Theoryテストでハマる! data class Fixture(val a: Int, val b: Int, val

    expected: Int) @RunWith(Theories::class) class CalculatorTest { private val sut = Calculator() @DataPoints fun getFixtures() = arrayOf(Fixture(1, 2, 3)) @Test fun `足し算結果を返すこと`(fixture: Fixture) { val got = sut.plus(fixture.a, fixture.b) assertThat(got).isEqualTo(fixture.expected) } } java.lang.Error: DataPoint field fixtures must be static
  3. Theoryテストでハマる! data class Fixture(val a: Int, val b: Int, val

    expected: Int) @RunWith(Theories::class) class CalculatorTest { private val sut = Calculator() @DataPoints fun getFixtures() = arrayOf(Fixture(1, 2, 3)) @Test fun `足し算結果を返すこと`(fixture: Fixture) { val got = sut.plus(fixture.a, fixture.b) assertThat(got).isEqualTo(fixture.expected) } } これをstaticにしたい java.lang.Error: DataPoint field fixtures must be static
  4. コンパニオンオブジェクト + @JvmStatic @RunWith(Theories::class) class CalculatorTest { companion object {

    @DataPoints @JvmStatic fun getFixtures() = arrayOf(Fixture(1, 2, 3) } JUnitが解釈してくれる!
  5. プロパティ + カスタムゲッターの場合 @RunWith(Theories::class) class CalculatorTest { companion object {

    @get:DataPoints @JvmStatic val fixtures: Array<Fixture> get() = arrayOf(Fixture(1, 2, 3) } getがミソ!
  6. AndroidのParcelableでも同じ問題が! data class Person(val name: String, val age: Int): Parcelable

    { companion object { @JvmField val CREATOR = object: Parcelable.Creator<Person> { override fun createFromParcel(p: Parcel): Person = Person(p.readString(), p.readInt()) } } ... } ←必要 KEEPにてデフォルトでのサポートが議論中 https://github.com/Kotlin/KEEP/pull/71
  7. Kotlin + Spring Boot @SpringBootApplication class DemoApplication { @Bean fun

    runner() = CommandLineRunner { println("Hello") } } @Configuration class 'DemoApplication' may not be final. • Kotlinはデフォルトでfinal。継承許可はopenを明示 • Springは多くの場面でopenを要求する
  8. Realmの場合 open class Product( var name: String? = null, var

    price: Double? = null ): RealmObject() 公式ドキュメント https://realm.io/docs/java/latest/ やはりopen必須...
  9. all-openコンパイラプラグイン buildscript { repositories { jcenter() } dependencies { classpath

    "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" } } apply plugin: 'kotlin-allopen' allOpen { annotation('io.realm.annotations.RealmClass') }
  10. all-openコンパイラプラグイン buildscript { repositories { jcenter() } dependencies { classpath

    "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" } } apply plugin: 'kotlin-allopen' allOpen { annotation('io.realm.annotations.RealmClass') } @RealmClassが付いたクラスをopenに
  11. さらばopen...! @RealmClass data class Product( var name: String? = null,

    var price: Double? = null ): RealmObject() 本来であればdataとopenは同居できないが、 all-openプラグインでこれが可能に(ヤバそう)
  12. なにこれ? @RealmClass data class Product( var name: String? = null,

    var price: Double? = null ): RealmObject() • Realmはデフォルトコンストラクタを要求する • でもnullにする予定ないし、そもそも面倒臭い
  13. no-argコンパイラプラグイン buildscript { repositories { jcenter() } dependencies { classpath

    "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version" } } apply plugin: 'kotlin-noarg' noArg { annotation('io.realm.annotations.RealmClass') } @RealmClassが付いたクラスに デフォルトコンストラクタを自動生成
  14. リフレクションでプロパティ名を取る val got = realm.where(Product::class.java) .equalTo(Product::name.name, "Apple") .greaterThan(Product::price.name, 0.4) .findFirst()

    「このプロパティならば、このデータ型の値が来る」 ということを保証できないのが怖くない?
  15. みんな大好き拡張関数 fun <T: RealmModel> RealmQuery<T>.equalTo( prop: KMutableProperty<T, String>, value: String

    ) = equalTo(property.name, value) fun <T: RealmModel> RealmQuery<T>.greaterThan( prop: KMutableProperty<T, Double>, value: Double ) = greaterThan(prop.name, value) その他、類似関数を多数定義
  16. ミスらない! val got = realm.where(Product::class.java) .equalTo(Product::name, "Apple") .greaterThan(Product::price, 0.4) .findFirst()

    • whereでProductを指定しているので、条件に使用でき るプロパティの持ち主もProductである必要がある • nameプロパティはStringなので、比較する値もString である必要がある
  17. まとめ • Kotlinにはstaticという概念がない ◦ @JvmStaticや@JvmFieldを上手く使う • FWのためだけに毎回openするのが面倒 ◦ Springならkotlin-springコンパイラプラグインを使う ◦

    all-openコンパイラプラグインを使う • デフォルトコンストラクタを提供したくない ◦ no-argコンパイラプラグインを使う