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/

4047c64e3a1e2f81addd4ba675ddc451?s=128

Marton Braun

June 29, 2021
Tweet

Transcript

  1. zsmb.co zsmb13 A Kotlin library design case study ft Krate

    Moshi Márton Braun
  2. Krate AutSoft/Krate Moshi square/moshi

  3. Krate AutSoft/Krate

  4. Krate interface Krate { val sharedPreferences: SharedPreferences }

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

    SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  6. 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) }
  7. 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) }
  8. 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) {
  9. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    intPref("appOpenCount") }
  10. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

    IntDelegate("appOpenCount") }
  11. Krate class MyKrate(context: Context) : SimpleKrate(context) { var appOpenCount by

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

    IntDelegate("appOpenCount") } class IntDelegate(private val key: String) : ReadWriteProperty<Krate, Int> { }
  13. 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) { } }
  14. 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) { } }
  15. 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) { } }
  16. 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") }
  17. 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") }
  18. 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) }
  19. 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) }
  20. Krate AutSoft/Krate

  21. Moshi square/moshi

  22. Moshi

  23. Moshi › Gson v3 in everything but name

  24. Moshi › Gson v3 in everything but name › Part

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

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

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

    of the Ok libraries › Streaming API › Nullable values
  28. 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, )
  29. 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()
  30. 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)
  31. 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\"}" )
  32. 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 $
  33. 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\"}" )
  34. 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) ?,
  35. 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 ?,
  36. 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!"
  37. 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!"
  38. 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()
  39. 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()
  40. 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()
  41. 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()
  42. 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...") } }
  43. 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
  44. 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
  45. Storing complex data

  46. DISCLAIMER Consider using a database before storing data like this

    in SharedPreferences
  47. Gson delegate

  48. Gson delegate fun <T : Any> Krate.gsonPref(key: String): ReadWriteProperty<Krate, T?>

    { return GsonDelegate(key) }
  49. 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) }
  50. 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) }
  51. 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) }
  52. 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) }
  53. 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
  54. 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) }
  55. 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) }
  56. 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) }
  57. 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,
  58. 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,
  59. 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) }
  60. 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
  61. Testing Krate

  62. Gson delegate tests data class TestModel(val x: Int, val y:

    Double, val z: String)
  63. 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)
  64. 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)
  65. 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)
  66. 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)
  67. 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)
  68. 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)
  69. ft Krate Moshi

  70. 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
  71. 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() } }
  72. 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() } }
  73. 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
  74. 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
  75. 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
  76. 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 ???)
  77. Class

  78. java.lang.Class

  79. java.lang.Class classes and interfaces in a running Java application

  80. java.lang.Class classes and interfaces in a running Java application Class

    c1 = object.getClass();
  81. java.lang.Class classes and interfaces in a running Java application Class

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

    c1 = object.getClass(); Class c2 = String.class; any.javaClass // Kotlin
  83. 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
  84. 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
  85. 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
  86. java.lang.reflect.Type

  87. java.lang.reflect.Type all types in the Java programming language - raw

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

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

    types - parameterized types - array types - type variables - primitive types Class String getTypeName();
  90. 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();
  91. Class Type No generics Supports generics Java reflection

  92. Class Type No generics Supports generics Java reflection Kotlin reflection

  93. Class Type No generics Supports generics Java reflection Kotlin reflection

    KClass
  94. kotlin.reflect.KClass

  95. kotlin.reflect.KClass val k1: KClass<*> = any::class val k2: KClass<*> =

    String::class
  96. 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
  97. 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
  98. val c: Class<*> = String::class.java kotlin.reflect.KClass val k1: KClass<*> =

    any::class val k2: KClass<*> = String::class
  99. val c: Class<*> = String::class.java kotlin.reflect. val k1: KClass<*> =

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

    KClass
  101. Class Type No generics Supports generics Java reflection Kotlin reflection

    KClass KType ParameterizedType
  102. Class Type No generics Supports generics Java reflection Kotlin reflection

    KClass KType ParameterizedType
  103. Class Type No generics Supports generics Java reflection Kotlin reflection

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

    KClass KType rawType actualTypeArguments ParameterizedType classifier arguments
  105. rawType actualTypeArguments ParameterizedType classifier arguments isMarkedNullable Class Type No generics

    Supports generics Java reflection Kotlin reflection KClass KType
  106. rawType actualTypeArguments ParameterizedType classifier arguments Class Type No generics Supports

    generics Java reflection Kotlin reflection KClass KType
  107. 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?> { ... }
  108. 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?> { ... }
  109. 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?> { ... }
  110. 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)
  111. 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)
  112. 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)
  113. 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)
  114. 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)
  115. 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)
  116. 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} ]
  117. 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} ]
  118. Actual :[ {x=42.0, y=3.141592, z=shibboleth}, {x=13.0, y=6.283185, z=signal} ]

  119. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

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

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

    { return MoshiDelegate(key, T::class.java) } Moshi delegate tests
  122. 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
  123. 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
  124. 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
  125. inline fun <reified T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?>

    { return MoshiDelegate(key, T::class.java) } Looking for a Type
  126. 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
  127. 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
  128. 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
  129. Looking for a Type inline fun <reified T : Any>

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

    Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, object : TypeToken<T>() {}.type) }
  131. TypeToken object : TypeToken<T>() {}.type

  132. 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
  133. 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
  134. 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
  135. 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
  136. 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();
  137. getGenericSuperclass(); * ... */ public Type /** * Returns the

    Type representing the direct superclass of the entity * represented by this Class. *
  138. * 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. *
  139. * 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. *
  140. 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();
  141. 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
  142. 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
  143. 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) { ... }
  144. 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 } }
  145. 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
  146. : 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
  147. 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
  148. 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
  149. TypeToken

  150. typeOf inline fun <reified T> makeType(): Type inline fun <reified

    T : Any> Krate.moshiPref(key: String): ReadWriteProperty<Krate, T?> { return MoshiDelegate(key, makeType<T>()) }
  151. 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>()) }
  152. 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
  153. 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
  154. 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
  155. 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
  156. 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
  157. 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
  158. 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") } }
  159. 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>()) }
  160. 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
  161. 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
  162. 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
  163. Resources

  164. • Krate  https://github.com/AutSoft/Krate • Moshi  https://github.com/square/moshi Resources

  165. • 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
  166. • 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
  167. zsmb13 zsmb.co/talks

  168. 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