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

Krate ft. Moshi (Kotlin Budapest Meetup 2021 June)

Krate ft. Moshi (Kotlin Budapest Meetup 2021 June)

Krate is a SharedPreferences wrapper library based on Kotlin's delegates. This talk takes you on a journey where you'll learn how the new Moshi-based addon for the library was implemented. We'll touch on type erasure and reflection, testing, debugging, and various advanced Kotlin features along the way.

Resources & more info: https://zsmb.co/talks/krate-ft-moshi/

Márton Braun

June 29, 2021
Tweet

More Decks by Márton Braun

Other Decks in Programming

Transcript

  1. Krate interface Krate { val sharedPreferences: SharedPreferences } abstract class

    SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  2. interface Krate { val sharedPreferences: SharedPreferences } Krate class MyKrate(context:

    Context) : SimpleKrate(context) { var onboarded by booleanPref("onboarded") var appOpenCount by intPref("appOpenCount") var username by stringPref("username") } abstract class SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  3. interface Krate { val sharedPreferences: SharedPreferences } Krate myKrate.onboarded =

    true myKrate.appOpenCount++ myKrate.username = "t1gg3r" class MyKrate(context: Context) : SimpleKrate(context) { var onboarded by booleanPref("onboarded") var appOpenCount by intPref("appOpenCount") var username by stringPref("username") } abstract class SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  4. Krate myKrate.onboarded = true myKrate.appOpenCount++ myKrate.username = "t1gg3r" var onboarded

    by booleanPref("onboarded") var username by stringPref("username") abstract class SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) } } var appOpenCount by intPref("appOpenCount") class MyKrate(context: Context) : SimpleKrate(context) {
  5. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    IntDelegate("appOpenCount") } class IntDelegate(private val key: String)
  6. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    IntDelegate("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { }
  7. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    IntDelegate("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { } }
  8. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    IntDelegate("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { } }
  9. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount: Int

    by IntDelegate("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { } }
  10. Krate class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> {

    override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { } } class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by IntDelegate("appOpenCount") }
  11. Krate class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> {

    override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return thisRef.sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { thisRef.sharedPreferences.edit().putInt(key, value).apply() } } class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by IntDelegate("appOpenCount") }
  12. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    IntDelegate("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return thisRef.sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { thisRef.sharedPreferences.edit().putInt(key, value).apply() } } fun Krate.intPref(key: String): ReadWriteProperty<Krate, Int> { return IntDelegate(key) }
  13. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    intPref("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return thisRef.sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { thisRef.sharedPreferences.edit().putInt(key, value).apply() } } fun Krate.intPref(key: String): ReadWriteProperty<Krate, Int> { return IntDelegate(key) }
  14. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries Okio Retrofit OkHttp Moshi
  15. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API Okio Retrofit OkHttp Moshi
  16. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API
  17. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values
  18. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String, )
  19. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String, ) val moshi = Moshi.Builder().build()
  20. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String, ) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java)
  21. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String, ) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" )
  22. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String, ) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Exception in thread "main" com.squareup.moshi.JsonDataException: Required value 'description' missing at $
  23. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String?, ) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" )
  24. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String ) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Burger(name=Cheeseburger, description=null) ?,
  25. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String ) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) › Default parameter values ?,
  26. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String ) , = "Yummy!"
  27. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Burger(name=Cheeseburger, description=Yummy!) @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String ) , = "Yummy!"
  28. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Codegen, reflection, or both @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String = "Yummy!", ) val moshi = Moshi.Builder().build()
  29. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Codegen, reflection, or both data class Burger( val name: String, val description: String = "Yummy!", ) val moshi = Moshi.Builder().build()
  30. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Codegen, reflection, or both data class Burger( val name: String, val description: String = "Yummy!", ) val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build()
  31. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Codegen, reflection, or both @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String = "Yummy!", ) val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build()
  32. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Codegen, reflection, or both › Always calls constructors @JsonClass(generateAdapter = true) data class Burger( val name: String, val description: String = "Yummy!", ) { init { println("$name coming up...") } }
  33. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Always calls constructors › Named after Jake’s dog › Codegen, reflection, or both
  34. Moshi › Gson v3 in everything but name › Part

    of the Ok libraries › Streaming API › Nullable values › Default parameter values › Always calls constructors › Named after Jake’s dog › Codegen, reflection, or both
  35. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) }
  36. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { } } fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) }
  37. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { } } fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) }
  38. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) }
  39. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) } java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to hu.autsoft.krate.gson.TestModel
  40. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) }
  41. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key) }
  42. Gson delegate class GsonDelegate<Object>( private val key: String, ) :

    ReadWriteProperty<Krate, Object> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Object? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<Object>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Object?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } fun <Object> Krate.gsonPref(key: String): ReadWriteProperty<Krate, Object?> { return GsonDelegate(key) }
  43. Object T Gson delegate class GsonDelegate<T : Any>( private val

    key: String, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } inline fun <reified T : Any> ?> { return GsonDelegate(key) } Krate.gsonPref(key: String): ReadWriteProperty<Krate,
  44. inline fun <reified T : Any> ?> { return GsonDelegate(key)

    } Object T Gson delegate class GsonDelegate<T : Any>( private val key: String, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } Krate.gsonPref(key: String): ReadWriteProperty<Krate,
  45. Gson delegate class GsonDelegate<T : Any>( private val key: String,

    ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, object : TypeToken<T>() {}.type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } inline fun <reified T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) }
  46. inline fun <reified T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?>

    { return GsonDelegate(key, object : TypeToken<T>() {}.type) } Gson delegate } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } class GsonDelegate<T : Any>( private val key: String, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, ) private val type: Type, type
  47. Gson delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } data class TestModel(val x: Int, val y: Double, val z: String)
  48. Gson delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  49. Gson delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } @Test fun testSimpleValue() { val model = TestModel(x = 42, y = 3.141592, z = "shibboleth") krate.simpleValue = model assertEquals(model, krate.simpleValue) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  50. Gson delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } @Test fun testSimpleValue() { val model = TestModel(x = 42, y = 3.141592, z = "shibboleth") krate.simpleValue = model assertEquals(model, krate.simpleValue) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  51. Gson delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } @Test fun testListOfValues() { val list = listOf( TestModel(x = 42, y = 3.141592, z = "shibboleth"), TestModel(x = 13, y = 6.283185, z = "signal"), ) krate.listOfValues = list assertEquals(list, krate.listOfValues) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  52. Gson delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } @Test fun testListOfValues() { val list = listOf( TestModel(x = 42, y = 3.141592, z = "shibboleth"), TestModel(x = 13, y = 6.283185, z = "signal"), ) krate.listOfValues = list assertEquals(list, krate.listOfValues) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  53. class GsonDelegate<T : Any>( private val key: String, private val

    type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val gson = Gson() val string = thisRef.sharedPreferences.getString(key, null) return gson.fromJson(string, type) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } } inline fun <reified T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } Moshi delegate
  54. inline fun <reified T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?>

    { return GsonDelegate(key, object : TypeToken<T>() {}.type) } Moshi delegate gson Gson() = .fromJson(string ) , type gson class MoshiDelegate<T : Any>( private val key: String, private val type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val val string = thisRef.sharedPreferences.getString(key, null) return } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } }
  55. inline fun <reified T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?>

    { return GsonDelegate(key, object : TypeToken<T>() {}.type) } Moshi delegate moshi.adapter<T?>(type) = adapter .fromJson(string) adapter class MoshiDelegate<T : Any>( private val key: String, private val type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val val string = thisRef.sharedPreferences.getString(key, null) return } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val gson = Gson() thisRef.sharedPreferences.edit() .putString(key, gson.toJson(value)) .apply() } }
  56. class MoshiDelegate<T : Any>( private val key: String, private val

    type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val adapter = moshi.adapter<T?>(type) val string = thisRef.sharedPreferences.getString(key, null) return adapter.fromJson(string) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val adapter = moshi.adapter<T?>(type) thisRef.sharedPreferences.edit() .putString(key, adapter.toJson(value)) .apply() } } inline fun <reified T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } Moshi delegate
  57. class MoshiDelegate<T : Any>( private val key: String, private val

    type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val adapter = moshi.adapter<T?>(type) val string = thisRef.sharedPreferences.getString(key, null) return adapter.fromJson(string) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val adapter = moshi.adapter<T?>(type) thisRef.sharedPreferences.edit() .putString(key, adapter.toJson(value)) .apply() } } inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, object : TypeToken<T>() {}.type) } Moshi delegate
  58. class MoshiDelegate<T : Any>( private val key: String, private val

    type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val adapter = moshi.adapter<T?>(type) val string = thisRef.sharedPreferences.getString(key, null) return adapter.fromJson(string) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val adapter = moshi.adapter<T?>(type) thisRef.sharedPreferences.edit() .putString(key, adapter.toJson(value)) .apply() } } inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, object : TypeToken<T>() {}.type) } Moshi delegate
  59. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, } class MoshiDelegate<T : Any>( private val key: String, private val type: Type, ) : ReadWriteProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val adapter = moshi.adapter<T?>(type) val string = thisRef.sharedPreferences.getString(key, null) return adapter.fromJson(string) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: T?) { val adapter = moshi.adapter<T?>(type) thisRef.sharedPreferences.edit() .putString(key, adapter.toJson(value)) .apply() } } Moshi delegate ???)
  60. java.lang.Class classes and interfaces in a running Java application Class

    c1 = object.getClass(); any.javaClass // Kotlin
  61. java.lang.Class classes and interfaces in a running Java application Class

    c1 = object.getClass(); Class c2 = String.class; any.javaClass // Kotlin
  62. java.lang.Class classes and interfaces in a running Java application Class

    c1 = object.getClass(); Class c2 = String.class; any.javaClass "hello world".getClass(); // class java.lang.String new ArrayList<String>().getClass(); // class java.util.ArrayList new Serializable() {}.getClass(); // class com.example.Main$1 // Kotlin
  63. java.lang.Class classes and interfaces in a running Java application Class

    c1 = object.getClass(); Class c2 = String.class; any.javaClass c.getName(); c.getSimpleName(); c.getCanonicalName(); c.getTypeName(); // Kotlin [I int[] int[] int[] java.lang.String String java.lang.String java.lang.String com.example.Main$1 null com.example.Main$1
  64. java.lang.Class classes and interfaces in a running Java application Class

    c1 = object.getClass(); Class c2 = String.class; any.javaClass c.getName(); c.getSimpleName(); c.getCanonicalName(); c.getTypeName(); Annotation[] annotations = c.getAnnotations(); Class[] interfaces = c.getInterfaces(); Constructor[] constructors = c.getConstructors(); Field[] fields = c.getFields(); Method[] methods = c.getMethods(); // Kotlin [I int[] int[] int[] java.lang.String String java.lang.String java.lang.String com.example.Main$1 null com.example.Main$1
  65. java.lang.reflect.Type all types in the Java programming language - raw

    types - parameterized types - array types - type variables - primitive types
  66. java.lang.reflect.Type all types in the Java programming language - raw

    types - parameterized types - array types - type variables - primitive types String getTypeName();
  67. java.lang.reflect.Type all types in the Java programming language - raw

    types - parameterized types - array types - type variables - primitive types Class String getTypeName();
  68. java.lang.reflect.Type all types in the Java programming language - raw

    types - parameterized types - array types - type variables - primitive types Class ParameterizedType Type getRawType(); Type[] getActualTypeArguments(); String getTypeName();
  69. kotlin.reflect.KClass val simpleName: String? val qualifiedName: String? val members: Collection<KCallable<*>>

    val constructors: Collection<KFunction<T>> val nestedClasses: Collection<KClass<*>> val typeParameters: List<KTypeParameter> val supertypes: List<KType> val sealedSubclasses: List<KClass<out T>> val visibility: KVisibility? val k1: KClass<*> = any::class val k2: KClass<*> = String::class
  70. kotlin.reflect.KClass val k1: KClass<*> = any::class val k2: KClass<*> =

    String::class val isFinal: Boolean val isOpen: Boolean val isAbstract: Boolean val isSealed: Boolean val isData: Boolean val isInner: Boolean val isCompanion: Boolean val isFun: Boolean
  71. val c: Class<*> = String::class.java kotlin.reflect. val k1: KClass<*> =

    any::class val k2: KClass<*> = String::class String.class // Java // Kotlin KClass
  72. Class Type No generics Supports generics Java reflection Kotlin reflection

    KClass KType rawType actualTypeArguments ParameterizedType
  73. Class Type No generics Supports generics Java reflection Kotlin reflection

    KClass KType rawType actualTypeArguments ParameterizedType classifier arguments
  74. Looking for a Type inline fun <reified T : Any>

    Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, ???) } class MoshiDelegate<T : Any>( private val key: String, private val type: Type, ) : ReadWriteProperty<Krate, T?> { ... }
  75. Looking for a Type inline fun <reified T : Any>

    Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, T::class.java) } class MoshiDelegate<T : Any>( private val key: String, private val type: Type, ) : ReadWriteProperty<Krate, T?> { ... }
  76. Looking for a Type inline fun <reified T : Any>

    Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, T::class.java) } class MoshiDelegate<T : Any>( private val key: String, private val type: Type, ) : ReadWriteProperty<Krate, T?> { ... }
  77. Moshi delegate tests class GsonTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by gsonPref("simpleValue") var listOfValues: List<TestModel>? by gsonPref("listOfValues") } @Test fun testSimpleValue() { val model = TestModel(x = 42, y = 3.141592, z = "shibboleth") krate.simpleValue = model assertEquals(model, krate.simpleValue) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  78. Moshi delegate tests class MoshiTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by moshiPref("simpleValue") var listOfValues: List<TestModel>? by moshiPref("listOfValues") } @Test fun testSimpleValue() { val model = TestModel(x = 42, y = 3.141592, z = "shibboleth") krate.simpleValue = model assertEquals(model, krate.simpleValue) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = GsonTestKrate(targetContext)
  79. Moshi delegate tests class MoshiTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by moshiPref("simpleValue") var listOfValues: List<TestModel>? by moshiPref("listOfValues") } @Test fun testSimpleValue() { val model = TestModel(x = 42, y = 3.141592, z = "shibboleth") krate.simpleValue = model assertEquals(model, krate.simpleValue) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = MoshiTestKrate(targetContext)
  80. Moshi delegate tests class MoshiTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by moshiPref("simpleValue") var listOfValues: List<TestModel>? by moshiPref("listOfValues") } @Test fun testSimpleValue() { val model = TestModel(x = 42, y = 3.141592, z = "shibboleth") krate.simpleValue = model assertEquals(model, krate.simpleValue) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = MoshiTestKrate(targetContext)
  81. Moshi delegate tests class MoshiTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by moshiPref("simpleValue") var listOfValues: List<TestModel>? by moshiPref("listOfValues") } @Test fun testListOfValues() { val list = listOf( TestModel(x = 42, y = 3.141592, z = "shibboleth"), TestModel(x = 13, y = 6.283185, z = "signal"), ) krate.listOfValues = list assertEquals(list, krate.listOfValues) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = MoshiTestKrate(targetContext)
  82. Moshi delegate tests class MoshiTestKrate(context: Context) : SimpleKrate(context) { var

    simpleValue: TestModel? by moshiPref("simpleValue") var listOfValues: List<TestModel>? by moshiPref("listOfValues") } @Test fun testListOfValues() { val list = listOf( TestModel(x = 42, y = 3.141592, z = "shibboleth"), TestModel(x = 13, y = 6.283185, z = "signal"), ) krate.listOfValues = list assertEquals(list, krate.listOfValues) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = MoshiTestKrate(targetContext)
  83. class MoshiTestKrate(context: Context) : SimpleKrate(context) { var simpleValue: TestModel? by

    moshiPref("simpleValue") var listOfValues: List<TestModel>? by moshiPref("listOfValues") } @Test fun testListOfValues() { val list = listOf( TestModel(x = 42, y = 3.141592, z = "shibboleth"), TestModel(x = 13, y = 6.283185, z = "signal"), ) krate.listOfValues = list assertEquals(list, krate.listOfValues) } data class TestModel(val x: Int, val y: Double, val z: String) val krate = MoshiTestKrate(targetContext) Moshi delegate tests java.lang.AssertionError: Expected :[ TestModel(x=42, y=3.141592, z=shibboleth), TestModel(x=13, y=6.283185, z=signal) ] Actual :[ {x=42.0, y=3.141592, z=shibboleth}, {x=13.0, y=6.283185, z=signal} ]
  84. java.lang.AssertionError: Expected :[ TestModel(x=42, y=3.141592, z=shibboleth), TestModel(x=13, y=6.283185, z=signal) ]

    Actual :[ {x=42.0, y=3.141592, z=shibboleth}, {x=13.0, y=6.283185, z=signal} ]
  85. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Moshi delegate tests
  86. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Moshi delegate tests
  87. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Moshi delegate tests
  88. var simpleValue by moshiPref<TestModel>("simpleValue") var listOfValues by moshiPref<List<TestModel>>("listOfValues") inline fun

    <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, T::class.java) } Moshi delegate tests
  89. var simpleValue by moshiPref<TestModel>("simpleValue") var listOfValues by moshiPref<List<TestModel>>("listOfValues") inline fun

    <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, T::class.java) } class hu.autsoft.krate.moshi.TestModel Moshi delegate tests
  90. class hu.autsoft.krate.moshi.TestModel interface java.util.List var simpleValue by moshiPref<TestModel>("simpleValue") var listOfValues

    by moshiPref<List<TestModel>>("listOfValues") inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, T::class.java) } Moshi delegate tests
  91. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Looking for a Type
  92. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Types.newParameterizedType(List::class.java, TestModel::class.java) Looking for a Type
  93. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Types.newParameterizedType(List::class.java, TestModel::class.java) java.util.List<hu.autsoft.krate.moshi.TestModel> Looking for a Type
  94. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate( key, Types.newParameterizedType(T::class.java, ???::class.java) ) } Types.newParameterizedType(List::class.java, TestModel::class.java) java.util.List<hu.autsoft.krate.moshi.TestModel> Looking for a Type
  95. Looking for a Type inline fun <reified T : Any>

    Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, } ) ???
  96. Looking for a Type inline fun <reified T : Any>

    Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, object : TypeToken<T>() {}.type) }
  97. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type
  98. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type
  99. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type
  100. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type
  101. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass. if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type getGenericSuperclass();
  102. getGenericSuperclass(); * ... */ public Type /** * Returns the

    Type representing the direct superclass of the entity * represented by this Class. *
  103. * If the superclass is a parameterized type, the Type

    object returned must * accurately reflect the actual type parameters used in the source code. * getGenericSuperclass(); * ... */ public Type /** * Returns the Type representing the direct superclass of the entity * represented by this Class. *
  104. * Non-parameterized -> Class * Parameterized -> ParameterizedType * If

    the superclass is a parameterized type, the Type object returned must * accurately reflect the actual type parameters used in the source code. * getGenericSuperclass(); * ... */ public Type /** * Returns the Type representing the direct superclass of the entity * represented by this Class. *
  105. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass. if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type getGenericSuperclass();
  106. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type
  107. TypeToken public class TypeToken<T> { final Type type = getSuperclassTypeParameter(getClass());

    protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return canonicalize(parameterized.getActualTypeArguments()[0]); } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... } } object : TypeToken<T>() {}.type
  108. TypeToken object : TypeToken<T>() {}.type public class TypeToken<T> { final

    Type type = getSuperclassTypeParameter(getClass()); protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; parameterized.getActualTypeArguments()[0] canonicalize( ); return } } /** * Returns a type that is functionally equal but not necessarily equal * according to Object.equals(). */ public static Type canonicalize(Type type) { ... }
  109. parameterized.getActualTypeArguments()[0] TypeToken object : TypeToken<T>() {}.type ; public class TypeToken<T>

    { final Type type = getSuperclassTypeParameter(getClass()); protected TypeToken() {} static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return } }
  110. open constructor val fun : * : val is <*>

    val as getGenericSuperclass() getActualTypeArguments() TypeToken object : TypeToken<T>() {}.type subclass getSuperclassTypeParameter Class< >) Type ( type = getSuperclassTypeParameter( ) class TypeToken<T> { { { } } } return parameterized. parameterized = () protected javaClass if (superclass Class superclass = subclass. ) superclass ParameterizedType RuntimeException("Missing type parameter.") throw [0] actualTypeArguments genericSuperclass
  111. : Type requireNotNull token::class.java if (superclass is Class<*>) { throw

    RuntimeException("Missing type parameter.") } val parameterized = superclass as ParameterizedType return parameterized.actualTypeArguments[0] } TypeToken object : TypeToken<T>() {}.type abstract ): Type { .genericSuperclass ( ) class TypeToken<T> val token = object : TypeToken<T>() {} inline <reified T> makeType = val superclass ( fun
  112. TypeToken makeType<T>() : Type requireNotNull token::class.java if (superclass is Class<*>)

    { throw RuntimeException("Missing type parameter.") } val parameterized = superclass as ParameterizedType return parameterized.actualTypeArguments[0] } abstract ): Type { .genericSuperclass ( ) class TypeToken<T> val token = object : TypeToken<T>() {} inline <reified T> makeType = val superclass ( fun
  113. TypeToken inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, ) } makeType<T>() : Type requireNotNull token::class.java if (superclass is Class<*>) { throw RuntimeException("Missing type parameter.") } val parameterized = superclass as ParameterizedType return parameterized.actualTypeArguments[0] } abstract ): Type { .genericSuperclass ( ) class TypeToken<T> val token = object : TypeToken<T>() {} inline <reified T> makeType = val superclass ( fun
  114. typeOf inline fun <reified T> makeType(): Type inline fun <reified

    T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, makeType<T>()) }
  115. typeOf inline fun <reified T> makeType(): Type /** * Returns

    a runtime representation of the given * reified type [T] as an instance of [KType]. */ inline fun <reified T> typeOf(): KType inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, makeType<T>()) }
  116. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>()) } /** * Returns a runtime representation of the given * reified type [T] as an instance of [KType]. */ inline fun <reified T> typeOf(): KType
  117. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>()) } /** * Returns a runtime representation of the given * reified type [T] as an instance of [KType]. */ inline fun <reified T> typeOf(): KType
  118. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>()) } /** * Returns a runtime representation of the given * reified type [T] as an instance of [KType]. */ inline fun <reified T> typeOf(): KType /** * Returns a Java [Type] instance corresponding * to the given Kotlin type. */ val KType.javaType: Type
  119. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>().javaType) } /** * Returns a runtime representation of the given * reified type [T] as an instance of [KType]. */ inline fun <reified T> typeOf(): KType /** * Returns a Java [Type] instance corresponding * to the given Kotlin type. */ val KType.javaType: Type
  120. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>().javaType) } /** * Returns a runtime representation of the given * reified type [T] as an instance of [KType]. */ inline fun <reified T> typeOf(): KType /** * Returns a Java [Type] instance corresponding * to the given Kotlin type. */ val KType.javaType: Type NotImplementedError: Java type is not yet supported for types created with createType
  121. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>().javaType) } /** * Returns a runtime representation of the given * reified type [T] as an instance of [KType]. */ /** * Returns a Java [Type] instance corresponding * to the given Kotlin type. */ val KType.javaType: Type inline fun <reified T> typeOf(): KType NotImplementedError: Java type is not yet supported for types created with createType
  122. makeType typeOf inline fun <reified T : Any> Krate.moshiPref(key: String):

    ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, typeOf<T>().javaType) } inline fun <reified T> typeOf(): KType val KType.javaType: Type fun KClassifier.createType(...): KType { ... return KTypeImpl(kotlinType) { TODO("Java type is not yet supported for types created with createType") } }
  123. typeOf inline fun <reified T> typeOf(): KType val KType.javaType: Type

    inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, makeType<T>()) }
  124. typeOf inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate,

    T?> { return MoshiDelegate(key, typeOf<T>().javaType) } inline fun <reified T> typeOf(): KType val KType.javaType: Type
  125. typeOf @OptIn(ExperimentalStdlibApi::class) inline fun <reified T : Any> Krate.moshiPref(key: String):

    ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, typeOf<T>().javaType) } inline fun <reified T> typeOf(): KType val KType.javaType: Type
  126. typeOf @OptIn(ExperimentalStdlibApi::class) inline fun <reified T : Any> Krate.moshiPref(key: String):

    ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, typeOf<T>().javaType) } inline fun <reified T> typeOf(): KType val KType.javaType: Type
  127. • Krate  https://github.com/AutSoft/Krate • Moshi  https://github.com/square/moshi • Delightful

    Delegate Design  https://www.droidcon.com/media-detail?video=481186381 • Say “hi” to Moshi!  https://www.youtube.com/watch?v=qadYVYc-8mc Resources
  128. • Krate  https://github.com/AutSoft/Krate • Moshi  https://github.com/square/moshi • Delightful

    Delegate Design  https://www.droidcon.com/media-detail?video=481186381 • Say “hi” to Moshi!  https://www.youtube.com/watch?v=qadYVYc-8mc • Library design articles  https://zsmb.co/maintaining-compatibility-in-kotlin-libraries/  https://zsmb.co/mastering-api-visibility-in-kotlin/  https://zsmb.co/talks/how-to-build-awesome-android-libraries/ Resources
  129. ft Krate Moshi A Kotlin library design case study zsmb.co/talks

    zsmb13 Márton Braun › Krate is great › Moshi is awesome › Type erasure is evil › Tests are your friend