Delegates, Delegates
everywhere
Миронов Владимир
Делегирование
Делегирование
● Очень часто используемый дизайн паттерн
Делегирование
● Очень часто используемый дизайн паттерн
● Kotlin поддерживает делегирование на уровне языка
Делегирование
● Очень часто используемый дизайн паттерн
● Kotlin поддерживает делегирование на уровне языка
● Два вида делегирования в Kotlin
Делегирование
● Очень часто используемый дизайн паттерн
● Kotlin поддерживает делегирование на уровне языка
● Два вида делегирования в Kotlin
● Interface delegation
Делегирование
● Очень часто используемый дизайн паттерн
● Kotlin поддерживает делегирование на уровне языка
● Два вида делегирования в Kotlin
● Interface delegation
● Property delegation
Properties
Properties
● Понятие field отсутсвует на уровне языка
Properties
● Понятие field отсутсвует на уровне языка
● Есть только property
Properties
● Понятие field отсутсвует на уровне языка
● Есть только property
● Пара setter + getter для var и getter для val
Properties
● Понятие field отсутсвует на уровне языка
● Есть только property
● Пара setter + getter для var и getter для val
● Все обращения происходят через getter и setter
Properties
● Понятие field отсутсвует на уровне языка
● Есть только property
● Пара setter + getter для var и getter для val
● Все обращения происходят через getter и setter
● При необходимости у property есть backing field
Property Delegation
Property Delegation
class HelloDelegates {
var property by Delegate()
}
Property Delegation
class HelloDelegates {
var property by Delegate()
}
class HelloDelegates {
private val delegate = Delegate()
}
Property Delegation
class HelloDelegates {
var property by Delegate()
}
class HelloDelegates {
private val delegate = Delegate()
var property: String
set(value) = delegate.setValue(this, metadata, value)
get() = delegate.getValue(this, metadata)
}
Property Delegation
class Delegate {
operator fun getValue(thisRef: Any, property: KProperty<*>): String
operator fun setValue(thisRef: Any, property: KProperty<*>, value: String)
}
Property Delegation
class Delegate {
operator fun getValue(thisRef: Any, property: KProperty<*>): String
operator fun setValue(thisRef: Any, property: KProperty<*>, value: String)
}
class Delegate : ReadOnlyProperty<Any, String>
class Delegate : ReadWriteProperty<Any, String>
Built-in delegates
● Delegates.notNull()
● lazy
● Delegates.observable()
● Delegates.vetoable
Delegates.notNull()
class ChatFragment : Fragment() {
override fun onAttach(activity: Activity) {
super.onAttach(activity)
adapter = createMessagesAdapter(context)
}
}
Delegates.notNull()
class ChatFragment : Fragment() {
private var adapter: MessagesAdapter? = null
override fun onAttach(activity: Activity) {
super.onAttach(activity)
adapter = createMessagesAdapter(context)
}
}
Delegates.notNull()
class ChatFragment : Fragment() {
private var adapter: MessagesAdapter? = null
override fun onAttach(activity: Activity) {
super.onAttach(activity)
adapter = createMessagesAdapter(context)
}
private fun onMessagesChanged(messages: List<Message>) {
adapter!!.notifyMessagedChanged(messages)
}
}
Delegates.notNull()
class ChatFragment : Fragment() {
private var adapter: MessagesAdapter by Delegates.notNull()
override fun onAttach(activity: Activity) {
super.onAttach(activity)
adapter = createMessagesAdapter(context)
}
private fun onMessagesChanged(messages: List<Message>) {
adapter!!.notifyMessagedChanged(messages)
}
}
Delegates.notNull()
class ChatFragment : Fragment() {
private var adapter: MessagesAdapter by Delegates.notNull()
override fun onAttach(activity: Activity) {
super.onAttach(activity)
adapter = createMessagesAdapter(context)
}
private fun onMessagesChanged(messages: List<Message>) {
adapter.notifyMessagedChanged(messages)
}
}
lazy
class ChatFragment : Fragment() {
private val adapter by lazy(LazyThreadSafetyMode.NONE) {
createMessagesAdapter(context)
}
private fun onMessagesChanged(messages: List<Message>) {
adapter.notifyMessagedChanged(messages)
}
}
Delegates.observable
class ChatFragment : Fragment() {
private var title by Delegates.observable("Chat") { desc, old, new ->
notifyTitleChanged(new)
}
private fun notifyTitleChanged(title: String) {
titleView.text = title
}
}
Наш первый делегат
public class OvalView extends View {
private float radiusX = 100.0f;
private float radiusY = 100.0f;
}
Наш первый делегат
public class OvalView extends View {
private float radiusX = 100.0f;
private float radiusY = 100.0f;
public void setRadiusX(float radiusX) {
this.radiusX = radiusX;
invalidate();
}
}
Наш первый делегат
public class OvalView extends View {
private float radiusX = 100.0f;
private float radiusY = 100.0f;
public void setRadiusX(float radiusX) {
this.radiusX = radiusX;
invalidate();
}
public void setRadiusY(float radiusY) {
this.radiusY = radiusY;
invalidate();
}
}
Наш первый делегат
class OvalView : View() {
val radiusX by Delegates.observable(100.0f) { desc, old, new ->
invalidate()
}
val radiusY by Delegates.observable(100.0f) { desc, old, new ->
invalidate()
}
}
Наш первый делегат
fun <T> View.bindViewProperty(initial: T): ReadWriteProperty<Any, T> {
return Delegates.observable(initial) { desc, old, new ->
invalidate()
}
}
Наш первый делегат
fun <T> View.bindViewProperty(initial: T): ReadWriteProperty<Any, T> {
return Delegates.observable(initial) { desc, old, new ->
invalidate()
}
}
class OvalView : View() {
val radiusX by bindViewProperty(100.0f)
val radiusY by bindViewProperty(100.0f)
}
Наш первый делегат
fun <T> View.bindViewProperty(initial: T): ReadWriteProperty<Any, T> {
return Delegates.observable(initial) { desc, old, new ->
invalidate()
}
}
class OvalView : View() {
val radiusX by bindViewProperty(100.0f)
val radiusY by bindViewProperty(100.0f)
val centerX by bindViewProperty(50.0f)
val centerY by bindViewProperty(50.0f)
}
Еще один простой делегат
fun <V : View> Activity.bindView(id: Int): Lazy<V> {
throw UnsupportedOperationException("Implement me!")
}
Еще один простой делегат
fun <V : View> Activity.bindView(id: Int): Lazy<V> {
return lazy(LazyThreadSafetyMode.NONE) {
findViewById(id) as V
}
}
Еще один простой делегат
fun <V : View> Activity.bindView(id: Int): Lazy<V> {
return lazy(LazyThreadSafetyMode.NONE) {
findViewById(id) as V
}
}
class UserActivity : Activity() {
private val image by bindView<ImageView>(R.id.user_image)
private val firstName by bindView<TextView>(R.id.user_first_name)
private val lastName by bindView<TextView>(R.id.user_last_name)
}
Typesafe Bundle Builders
Typesafe Bundle Builders
val fragment = UserFragment()
val extras = Bundle()
extras.putString("firstName", "Ivan")
extras.putString("lastName", "Ivanov")
extras.putInt("age", 20)
fragment.arguments = extras
Typesafe Bundle Builders
val fragment = UserFragment()
val extras = Bundle()
extras.putString("firstName", "Ivan")
extras.putString("lastName", "Ivanov")
extras.putInt("age", 20)
fragment.arguments = extras
inline fun <reified T : Any> bindArgument(bundle: Bundle, default: T? = null): BundleDelegate {
throw UnsupportedOperationException()
}
Typesafe Bundle Builders
class UserArguments(val extras: Bundle) {
var firstName by bindArgument<String>(extras)
var lastName by bindArgument<String>(extras)
var age by bindArgument(extras, 33)
}
Typesafe Bundle Builders
class UserArguments(val extras: Bundle) {
var firstName by bindArgument<String>(extras)
var lastName by bindArgument<String>(extras)
var age by bindArgument(extras, 33)
}
val fragment = UserFragment()
val arguments = UserArguments(Bundle())
arguments.firstName = "Ivan"
arguments.lastName = "Ivanov"
fragment.arguments = arguments.extras
Typesafe Bundle Builders
class UserFragment : Fragment() {
private val args by lazy(LazyThreadSafetyMode.NONE) {
UserArguments(arguments)
}
}
LifecycleAware delegates
class ChatFragment : BaseFragment() {
private var chat by Delegates.notNull<ChatModel>()
private var messages by Delegates.notNull<CollectionRange<Message>>()
}
LifecycleAware delegates
class ChatFragment : BaseFragment() {
private var chat by Delegates.notNull<ChatModel>()
private var messages by Delegates.notNull<CollectionRange<Message>>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
chat = acquireChatModel()
messages = chat.acquireMessagesRange()
}
}
LifecycleAware delegates
class ChatFragment : BaseFragment() {
private var chat by Delegates.notNull<ChatModel>()
private var messages by Delegates.notNull<CollectionRange<Message>>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
chat = acquireChatModel()
messages = chat.acquireMessagesRange()
}
override fun onDestroy() {
messages.close()
chat.close()
super.onDestroy()
}
}
LifecycleAware delegates
class ChatFragment : BaseFragment() {
private val chat by bindToLifecycleEagerly(LifecycleInterval.CREATED) {
acquireChatModel()
}
private val participants by bindToLifecycleEagerly(LifecycleInterval.CREATED) {
chat.acquireParticipantsRange()
}
}
Preferences delegates
class DebugSettings(private val preferences: SharedPreferences) {
var logEnabled: Boolean
set(value) = preferences.edit().putBoolean("logEnabled", value).apply()
get() = preferences.getBoolean("logEnabled", true)
var logTag: String
set(value) = preferences.edit().putString("logTag", value).apply()
get() = preferences.getString("logTag", "Debug")
}
Preferences delegates
class DebugSettings(private val preferences: SharedPreferences) {
var logEnabled by bindPreference(preferences, true)
var logTag by bindPreference(preferences, "Debug")
}
ReadWriteProperty и ReadOnlyProperty
ReadWriteProperty и ReadOnlyProperty
public interface ReadWriteProperty<in R, T> {
public operator fun getValue(thisRef: R, property: KProperty<*>): T
public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
public interface ReadOnlyProperty<in R, out T> {
public operator fun getValue(thisRef: R, property: KProperty<*>): T
}
KProperty<T>
KProperty<T>
● Хранит в себе метаинформацию о property
KProperty<T>
● Хранит в себе метаинформацию о property
● KProperty<T>.name: String
KProperty<T>
● Хранит в себе метаинформацию о property
● KProperty<T>.name: String
● KProperty<T>.returnType.javaType: Class
KProperty<T>
● Хранит в себе метаинформацию о property
● KProperty<T>.name: String
● KProperty<T>.returnType.javaType: Class
● KProperty<T>.returnType.isMarkedNullable: Boolean
KProperty<T>
● Хранит в себе метаинформацию о property
● KProperty<T>.name: String
● KProperty<T>.returnType.javaType: Class
● KProperty<T>.returnType.isMarkedNullable: Boolean
● KProperty<T>.annotations: List<Annotation>
KProperty<T> и Android
KProperty<T> и Android
● По дефолту работает только KProperty.name
KProperty<T> и Android
● По дефолту работает только KProperty.name
● Оставшимся методам необходима зависимость kotlin-reflect
KProperty<T> и Android
● По дефолту работает только KProperty.name
● Оставшимся методам необходима зависимость kotlin-reflect
● Количество методов - 12112
KProperty<T> и Android
● По дефолту работает только KProperty.name
● Оставшимся методам необходима зависимость kotlin-reflect
● Количество методов - 12112
● JAR Size - 2268KB
KProperty<T> и Android
● По дефолту работает только KProperty.name
● Оставшимся методам необходима зависимость kotlin-reflect
● Количество методов - 12112
● JAR Size - 2268KB
● DEX size - 1726KB
Как жить без reflection
Как жить без reflection
● KProperty.name работает и так
Как жить без reflection
● KProperty.name работает и так
● KProperty.returnType.javaType - inline reified generic функция
Как жить без reflection
● KProperty.name работает и так
● KProperty.returnType.javaType - inline reified generic функция
● KProperty.returnType.isMarkedNullable - два разных делегата
Как жить без reflection
● KProperty.name работает и так
● KProperty.returnType.javaType - inline reified generic функция
● KProperty.returnType.isMarkedNullable - два разных делегата
inline fun <reified T : Any> bindValue(): ReadWriteProperty<Any, T> {
return createRequiredDelegate(T::class.java)
}
Как жить без reflection
● KProperty.name работает и так
● KProperty.returnType.javaType - inline reified generic функция
● KProperty.returnType.isMarkedNullable - два разных делегата
inline fun <reified T : Any> bindValue(): ReadWriteProperty<Any, T> {
return createRequiredDelegate(T::class.java)
}
inline fun <reified T : Any> bindOptionalValue(): ReadWriteProperty<Any, T?> {
return createOptionalDelegate(T::class.java)
}
Preferences delegates
class DebugSettings(private val preferences: SharedPreferences) {
var logEnabled by bindPreference(preferences, true)
var logTag by bindPreference(preferences, "Debug")
}
Preferences delegates
class PreferenceReadWriteProperty<T : Any>(
// some arguments here
) : ReadWriteProperty<Any, T> {
// implement me
}
Preferences delegates
class PreferenceReadWriteProperty<T : Any>(
private val preferences: SharedPreferences
) : ReadWriteProperty<Any, T> {
// implement me
}
Preferences delegates
class PreferenceReadWriteProperty<T : Any>(
private val preferences: SharedPreferences,
private val clazz: Class<T>
) : ReadWriteProperty<Any, T> {
// implement me
}
Preferences delegates
class PreferenceReadWriteProperty<T : Any>(
private val preferences: SharedPreferences,
private val clazz: Class<T>,
private val default: T
) : ReadWriteProperty<Any, T> {
// implement me
}
Preferences delegates
class PreferenceReadWriteProperty<T : Any>(
private val preferences: SharedPreferences,
private val clazz: Class<T>,
private val default: T,
private val name: String?
) : ReadWriteProperty<Any, T> {
// implement me
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
throw UnsupportedOperationException()
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
if (!preferences.contains(key)) {
return default
}
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
if (!preferences.contains(key)) {
return default
}
return when (clazz) {
String::class.java -> preferences.getString(key, null)
else -> throw UnsupportedOperationException()
} as T
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
if (!preferences.contains(key)) {
return default
}
return when (clazz) {
String::class.java -> preferences.getString(key, null)
Int::class.java -> preferences.getInt(key, 0)
else -> throw UnsupportedOperationException()
} as T
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
if (!preferences.contains(key)) {
return default
}
return when (clazz) {
String::class.java -> preferences.getString(key, null)
Int::class.java -> preferences.getInt(key, 0)
Long::class.java -> preferences.getLong(key, 0L)
else -> throw UnsupportedOperationException()
} as T
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
if (!preferences.contains(key)) {
return default
}
return when (clazz) {
String::class.java -> preferences.getString(key, null)
Int::class.java -> preferences.getInt(key, 0)
Long::class.java -> preferences.getLong(key, 0L)
Boolean::class.java -> preferences.getBoolean(key, false)
else -> throw UnsupportedOperationException()
} as T
}
Preferences delegates
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val key = name ?: property.name
if (!preferences.contains(key)) {
return default
}
return when (clazz) {
String::class.java -> preferences.getString(key, null)
Int::class.java -> preferences.getInt(key, 0)
Long::class.java -> preferences.getLong(key, 0L)
Boolean::class.java -> preferences.getBoolean(key, false)
Float::class.java -> preferences.getFloat(key, 0.0f)
else -> throw UnsupportedOperationException()
} as T
}
Preferences delegates
inline fun <reified T : Any> bindPreference(
preferences: SharedPreferences,
default: T,
name: String? = null
) : ReadWriteProperty<Any, T> {
return PreferenceReadWriteProperty(preferences, T::class.java, default, name)
}
Preferences delegates
class DebugSettings(private val preferences: SharedPreferences) {
var logEnabled by bindPreference(preferences, true)
var logTag by bindPreference(preferences, "Debug")
}
Links
https://kotlinlang.org/docs/reference/delegated-properties.html
https://github.com/JakeWharton/kotterknife
https://github.com/nsk-mironov/kotlin-jetpack

​"Delegates, Delegates everywhere" Владимир Миронов

  • 1.
  • 2.
  • 3.
    Делегирование ● Очень частоиспользуемый дизайн паттерн
  • 4.
    Делегирование ● Очень частоиспользуемый дизайн паттерн ● Kotlin поддерживает делегирование на уровне языка
  • 5.
    Делегирование ● Очень частоиспользуемый дизайн паттерн ● Kotlin поддерживает делегирование на уровне языка ● Два вида делегирования в Kotlin
  • 6.
    Делегирование ● Очень частоиспользуемый дизайн паттерн ● Kotlin поддерживает делегирование на уровне языка ● Два вида делегирования в Kotlin ● Interface delegation
  • 7.
    Делегирование ● Очень частоиспользуемый дизайн паттерн ● Kotlin поддерживает делегирование на уровне языка ● Два вида делегирования в Kotlin ● Interface delegation ● Property delegation
  • 8.
  • 9.
    Properties ● Понятие fieldотсутсвует на уровне языка
  • 10.
    Properties ● Понятие fieldотсутсвует на уровне языка ● Есть только property
  • 11.
    Properties ● Понятие fieldотсутсвует на уровне языка ● Есть только property ● Пара setter + getter для var и getter для val
  • 12.
    Properties ● Понятие fieldотсутсвует на уровне языка ● Есть только property ● Пара setter + getter для var и getter для val ● Все обращения происходят через getter и setter
  • 13.
    Properties ● Понятие fieldотсутсвует на уровне языка ● Есть только property ● Пара setter + getter для var и getter для val ● Все обращения происходят через getter и setter ● При необходимости у property есть backing field
  • 14.
  • 15.
    Property Delegation class HelloDelegates{ var property by Delegate() }
  • 16.
    Property Delegation class HelloDelegates{ var property by Delegate() } class HelloDelegates { private val delegate = Delegate() }
  • 17.
    Property Delegation class HelloDelegates{ var property by Delegate() } class HelloDelegates { private val delegate = Delegate() var property: String set(value) = delegate.setValue(this, metadata, value) get() = delegate.getValue(this, metadata) }
  • 18.
    Property Delegation class Delegate{ operator fun getValue(thisRef: Any, property: KProperty<*>): String operator fun setValue(thisRef: Any, property: KProperty<*>, value: String) }
  • 19.
    Property Delegation class Delegate{ operator fun getValue(thisRef: Any, property: KProperty<*>): String operator fun setValue(thisRef: Any, property: KProperty<*>, value: String) } class Delegate : ReadOnlyProperty<Any, String> class Delegate : ReadWriteProperty<Any, String>
  • 20.
    Built-in delegates ● Delegates.notNull() ●lazy ● Delegates.observable() ● Delegates.vetoable
  • 21.
    Delegates.notNull() class ChatFragment :Fragment() { override fun onAttach(activity: Activity) { super.onAttach(activity) adapter = createMessagesAdapter(context) } }
  • 22.
    Delegates.notNull() class ChatFragment :Fragment() { private var adapter: MessagesAdapter? = null override fun onAttach(activity: Activity) { super.onAttach(activity) adapter = createMessagesAdapter(context) } }
  • 23.
    Delegates.notNull() class ChatFragment :Fragment() { private var adapter: MessagesAdapter? = null override fun onAttach(activity: Activity) { super.onAttach(activity) adapter = createMessagesAdapter(context) } private fun onMessagesChanged(messages: List<Message>) { adapter!!.notifyMessagedChanged(messages) } }
  • 24.
    Delegates.notNull() class ChatFragment :Fragment() { private var adapter: MessagesAdapter by Delegates.notNull() override fun onAttach(activity: Activity) { super.onAttach(activity) adapter = createMessagesAdapter(context) } private fun onMessagesChanged(messages: List<Message>) { adapter!!.notifyMessagedChanged(messages) } }
  • 25.
    Delegates.notNull() class ChatFragment :Fragment() { private var adapter: MessagesAdapter by Delegates.notNull() override fun onAttach(activity: Activity) { super.onAttach(activity) adapter = createMessagesAdapter(context) } private fun onMessagesChanged(messages: List<Message>) { adapter.notifyMessagedChanged(messages) } }
  • 26.
    lazy class ChatFragment :Fragment() { private val adapter by lazy(LazyThreadSafetyMode.NONE) { createMessagesAdapter(context) } private fun onMessagesChanged(messages: List<Message>) { adapter.notifyMessagedChanged(messages) } }
  • 27.
    Delegates.observable class ChatFragment :Fragment() { private var title by Delegates.observable("Chat") { desc, old, new -> notifyTitleChanged(new) } private fun notifyTitleChanged(title: String) { titleView.text = title } }
  • 28.
    Наш первый делегат publicclass OvalView extends View { private float radiusX = 100.0f; private float radiusY = 100.0f; }
  • 29.
    Наш первый делегат publicclass OvalView extends View { private float radiusX = 100.0f; private float radiusY = 100.0f; public void setRadiusX(float radiusX) { this.radiusX = radiusX; invalidate(); } }
  • 30.
    Наш первый делегат publicclass OvalView extends View { private float radiusX = 100.0f; private float radiusY = 100.0f; public void setRadiusX(float radiusX) { this.radiusX = radiusX; invalidate(); } public void setRadiusY(float radiusY) { this.radiusY = radiusY; invalidate(); } }
  • 31.
    Наш первый делегат classOvalView : View() { val radiusX by Delegates.observable(100.0f) { desc, old, new -> invalidate() } val radiusY by Delegates.observable(100.0f) { desc, old, new -> invalidate() } }
  • 32.
    Наш первый делегат fun<T> View.bindViewProperty(initial: T): ReadWriteProperty<Any, T> { return Delegates.observable(initial) { desc, old, new -> invalidate() } }
  • 33.
    Наш первый делегат fun<T> View.bindViewProperty(initial: T): ReadWriteProperty<Any, T> { return Delegates.observable(initial) { desc, old, new -> invalidate() } } class OvalView : View() { val radiusX by bindViewProperty(100.0f) val radiusY by bindViewProperty(100.0f) }
  • 34.
    Наш первый делегат fun<T> View.bindViewProperty(initial: T): ReadWriteProperty<Any, T> { return Delegates.observable(initial) { desc, old, new -> invalidate() } } class OvalView : View() { val radiusX by bindViewProperty(100.0f) val radiusY by bindViewProperty(100.0f) val centerX by bindViewProperty(50.0f) val centerY by bindViewProperty(50.0f) }
  • 35.
    Еще один простойделегат fun <V : View> Activity.bindView(id: Int): Lazy<V> { throw UnsupportedOperationException("Implement me!") }
  • 36.
    Еще один простойделегат fun <V : View> Activity.bindView(id: Int): Lazy<V> { return lazy(LazyThreadSafetyMode.NONE) { findViewById(id) as V } }
  • 37.
    Еще один простойделегат fun <V : View> Activity.bindView(id: Int): Lazy<V> { return lazy(LazyThreadSafetyMode.NONE) { findViewById(id) as V } } class UserActivity : Activity() { private val image by bindView<ImageView>(R.id.user_image) private val firstName by bindView<TextView>(R.id.user_first_name) private val lastName by bindView<TextView>(R.id.user_last_name) }
  • 38.
  • 39.
    Typesafe Bundle Builders valfragment = UserFragment() val extras = Bundle() extras.putString("firstName", "Ivan") extras.putString("lastName", "Ivanov") extras.putInt("age", 20) fragment.arguments = extras
  • 40.
    Typesafe Bundle Builders valfragment = UserFragment() val extras = Bundle() extras.putString("firstName", "Ivan") extras.putString("lastName", "Ivanov") extras.putInt("age", 20) fragment.arguments = extras inline fun <reified T : Any> bindArgument(bundle: Bundle, default: T? = null): BundleDelegate { throw UnsupportedOperationException() }
  • 41.
    Typesafe Bundle Builders classUserArguments(val extras: Bundle) { var firstName by bindArgument<String>(extras) var lastName by bindArgument<String>(extras) var age by bindArgument(extras, 33) }
  • 42.
    Typesafe Bundle Builders classUserArguments(val extras: Bundle) { var firstName by bindArgument<String>(extras) var lastName by bindArgument<String>(extras) var age by bindArgument(extras, 33) } val fragment = UserFragment() val arguments = UserArguments(Bundle()) arguments.firstName = "Ivan" arguments.lastName = "Ivanov" fragment.arguments = arguments.extras
  • 43.
    Typesafe Bundle Builders classUserFragment : Fragment() { private val args by lazy(LazyThreadSafetyMode.NONE) { UserArguments(arguments) } }
  • 44.
    LifecycleAware delegates class ChatFragment: BaseFragment() { private var chat by Delegates.notNull<ChatModel>() private var messages by Delegates.notNull<CollectionRange<Message>>() }
  • 45.
    LifecycleAware delegates class ChatFragment: BaseFragment() { private var chat by Delegates.notNull<ChatModel>() private var messages by Delegates.notNull<CollectionRange<Message>>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) chat = acquireChatModel() messages = chat.acquireMessagesRange() } }
  • 46.
    LifecycleAware delegates class ChatFragment: BaseFragment() { private var chat by Delegates.notNull<ChatModel>() private var messages by Delegates.notNull<CollectionRange<Message>>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) chat = acquireChatModel() messages = chat.acquireMessagesRange() } override fun onDestroy() { messages.close() chat.close() super.onDestroy() } }
  • 47.
    LifecycleAware delegates class ChatFragment: BaseFragment() { private val chat by bindToLifecycleEagerly(LifecycleInterval.CREATED) { acquireChatModel() } private val participants by bindToLifecycleEagerly(LifecycleInterval.CREATED) { chat.acquireParticipantsRange() } }
  • 48.
    Preferences delegates class DebugSettings(privateval preferences: SharedPreferences) { var logEnabled: Boolean set(value) = preferences.edit().putBoolean("logEnabled", value).apply() get() = preferences.getBoolean("logEnabled", true) var logTag: String set(value) = preferences.edit().putString("logTag", value).apply() get() = preferences.getString("logTag", "Debug") }
  • 49.
    Preferences delegates class DebugSettings(privateval preferences: SharedPreferences) { var logEnabled by bindPreference(preferences, true) var logTag by bindPreference(preferences, "Debug") }
  • 50.
  • 51.
    ReadWriteProperty и ReadOnlyProperty publicinterface ReadWriteProperty<in R, T> { public operator fun getValue(thisRef: R, property: KProperty<*>): T public operator fun setValue(thisRef: R, property: KProperty<*>, value: T) } public interface ReadOnlyProperty<in R, out T> { public operator fun getValue(thisRef: R, property: KProperty<*>): T }
  • 52.
  • 53.
    KProperty<T> ● Хранит всебе метаинформацию о property
  • 54.
    KProperty<T> ● Хранит всебе метаинформацию о property ● KProperty<T>.name: String
  • 55.
    KProperty<T> ● Хранит всебе метаинформацию о property ● KProperty<T>.name: String ● KProperty<T>.returnType.javaType: Class
  • 56.
    KProperty<T> ● Хранит всебе метаинформацию о property ● KProperty<T>.name: String ● KProperty<T>.returnType.javaType: Class ● KProperty<T>.returnType.isMarkedNullable: Boolean
  • 57.
    KProperty<T> ● Хранит всебе метаинформацию о property ● KProperty<T>.name: String ● KProperty<T>.returnType.javaType: Class ● KProperty<T>.returnType.isMarkedNullable: Boolean ● KProperty<T>.annotations: List<Annotation>
  • 58.
  • 59.
    KProperty<T> и Android ●По дефолту работает только KProperty.name
  • 60.
    KProperty<T> и Android ●По дефолту работает только KProperty.name ● Оставшимся методам необходима зависимость kotlin-reflect
  • 61.
    KProperty<T> и Android ●По дефолту работает только KProperty.name ● Оставшимся методам необходима зависимость kotlin-reflect ● Количество методов - 12112
  • 62.
    KProperty<T> и Android ●По дефолту работает только KProperty.name ● Оставшимся методам необходима зависимость kotlin-reflect ● Количество методов - 12112 ● JAR Size - 2268KB
  • 63.
    KProperty<T> и Android ●По дефолту работает только KProperty.name ● Оставшимся методам необходима зависимость kotlin-reflect ● Количество методов - 12112 ● JAR Size - 2268KB ● DEX size - 1726KB
  • 64.
  • 65.
    Как жить безreflection ● KProperty.name работает и так
  • 66.
    Как жить безreflection ● KProperty.name работает и так ● KProperty.returnType.javaType - inline reified generic функция
  • 67.
    Как жить безreflection ● KProperty.name работает и так ● KProperty.returnType.javaType - inline reified generic функция ● KProperty.returnType.isMarkedNullable - два разных делегата
  • 68.
    Как жить безreflection ● KProperty.name работает и так ● KProperty.returnType.javaType - inline reified generic функция ● KProperty.returnType.isMarkedNullable - два разных делегата inline fun <reified T : Any> bindValue(): ReadWriteProperty<Any, T> { return createRequiredDelegate(T::class.java) }
  • 69.
    Как жить безreflection ● KProperty.name работает и так ● KProperty.returnType.javaType - inline reified generic функция ● KProperty.returnType.isMarkedNullable - два разных делегата inline fun <reified T : Any> bindValue(): ReadWriteProperty<Any, T> { return createRequiredDelegate(T::class.java) } inline fun <reified T : Any> bindOptionalValue(): ReadWriteProperty<Any, T?> { return createOptionalDelegate(T::class.java) }
  • 70.
    Preferences delegates class DebugSettings(privateval preferences: SharedPreferences) { var logEnabled by bindPreference(preferences, true) var logTag by bindPreference(preferences, "Debug") }
  • 71.
    Preferences delegates class PreferenceReadWriteProperty<T: Any>( // some arguments here ) : ReadWriteProperty<Any, T> { // implement me }
  • 72.
    Preferences delegates class PreferenceReadWriteProperty<T: Any>( private val preferences: SharedPreferences ) : ReadWriteProperty<Any, T> { // implement me }
  • 73.
    Preferences delegates class PreferenceReadWriteProperty<T: Any>( private val preferences: SharedPreferences, private val clazz: Class<T> ) : ReadWriteProperty<Any, T> { // implement me }
  • 74.
    Preferences delegates class PreferenceReadWriteProperty<T: Any>( private val preferences: SharedPreferences, private val clazz: Class<T>, private val default: T ) : ReadWriteProperty<Any, T> { // implement me }
  • 75.
    Preferences delegates class PreferenceReadWriteProperty<T: Any>( private val preferences: SharedPreferences, private val clazz: Class<T>, private val default: T, private val name: String? ) : ReadWriteProperty<Any, T> { // implement me }
  • 76.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { throw UnsupportedOperationException() }
  • 77.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name }
  • 78.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default } }
  • 79.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default } return when (clazz) { String::class.java -> preferences.getString(key, null) else -> throw UnsupportedOperationException() } as T }
  • 80.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default } return when (clazz) { String::class.java -> preferences.getString(key, null) Int::class.java -> preferences.getInt(key, 0) else -> throw UnsupportedOperationException() } as T }
  • 81.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default } return when (clazz) { String::class.java -> preferences.getString(key, null) Int::class.java -> preferences.getInt(key, 0) Long::class.java -> preferences.getLong(key, 0L) else -> throw UnsupportedOperationException() } as T }
  • 82.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default } return when (clazz) { String::class.java -> preferences.getString(key, null) Int::class.java -> preferences.getInt(key, 0) Long::class.java -> preferences.getLong(key, 0L) Boolean::class.java -> preferences.getBoolean(key, false) else -> throw UnsupportedOperationException() } as T }
  • 83.
    Preferences delegates override fungetValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default } return when (clazz) { String::class.java -> preferences.getString(key, null) Int::class.java -> preferences.getInt(key, 0) Long::class.java -> preferences.getLong(key, 0L) Boolean::class.java -> preferences.getBoolean(key, false) Float::class.java -> preferences.getFloat(key, 0.0f) else -> throw UnsupportedOperationException() } as T }
  • 84.
    Preferences delegates inline fun<reified T : Any> bindPreference( preferences: SharedPreferences, default: T, name: String? = null ) : ReadWriteProperty<Any, T> { return PreferenceReadWriteProperty(preferences, T::class.java, default, name) }
  • 85.
    Preferences delegates class DebugSettings(privateval preferences: SharedPreferences) { var logEnabled by bindPreference(preferences, true) var logTag by bindPreference(preferences, "Debug") }
  • 86.