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

Post on 11-Apr-2017

386 views 7 download

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

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 Delegationclass HelloDelegates { var property by Delegate()}

Property Delegationclass HelloDelegates { var property by Delegate()}

class HelloDelegates { private val delegate = Delegate()}

Property Delegationclass 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 Delegationclass Delegate { operator fun getValue(thisRef: Any, property: KProperty<*>): String operator fun setValue(thisRef: Any, property: KProperty<*>, value: String)}

Property Delegationclass 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) }}

lazyclass ChatFragment : Fragment() { private val adapter by lazy(LazyThreadSafetyMode.NONE) { createMessagesAdapter(context) }

private fun onMessagesChanged(messages: List<Message>) { adapter.notifyMessagedChanged(messages) }}

Delegates.observableclass 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 Buildersval fragment = UserFragment()val extras = Bundle()

extras.putString("firstName", "Ivan")extras.putString("lastName", "Ivanov")extras.putInt("age", 20)

fragment.arguments = extras

Typesafe Bundle Buildersval 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 Buildersclass UserArguments(val extras: Bundle) { var firstName by bindArgument<String>(extras) var lastName by bindArgument<String>(extras) var age by bindArgument(extras, 33)}

Typesafe Bundle Buildersclass 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 Buildersclass UserFragment : Fragment() { private val args by lazy(LazyThreadSafetyMode.NONE) { UserArguments(arguments) }}

LifecycleAware delegatesclass ChatFragment : BaseFragment() { private var chat by Delegates.notNull<ChatModel>() private var messages by Delegates.notNull<CollectionRange<Message>>()}

LifecycleAware delegatesclass 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 delegatesclass 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 delegatesclass ChatFragment : BaseFragment() { private val chat by bindToLifecycleEagerly(LifecycleInterval.CREATED) { acquireChatModel() }

private val participants by bindToLifecycleEagerly(LifecycleInterval.CREATED) { chat.acquireParticipantsRange() }}

Preferences delegatesclass 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 delegatesclass DebugSettings(private val preferences: SharedPreferences) { var logEnabled by bindPreference(preferences, true) var logTag by bindPreference(preferences, "Debug")}

ReadWriteProperty и ReadOnlyProperty

ReadWriteProperty и ReadOnlyPropertypublic 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 delegatesclass DebugSettings(private val preferences: SharedPreferences) { var logEnabled by bindPreference(preferences, true) var logTag by bindPreference(preferences, "Debug")}

Preferences delegatesclass PreferenceReadWriteProperty<T : Any>( // some arguments here) : ReadWriteProperty<Any, T> { // implement me}

Preferences delegatesclass PreferenceReadWriteProperty<T : Any>( private val preferences: SharedPreferences) : ReadWriteProperty<Any, T> { // implement me}

Preferences delegatesclass PreferenceReadWriteProperty<T : Any>( private val preferences: SharedPreferences, private val clazz: Class<T>) : ReadWriteProperty<Any, T> { // implement me}

Preferences delegatesclass PreferenceReadWriteProperty<T : Any>( private val preferences: SharedPreferences, private val clazz: Class<T>, private val default: T) : ReadWriteProperty<Any, T> { // implement me}

Preferences delegatesclass 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 delegatesoverride fun getValue(thisRef: Any, property: KProperty<*>): T { throw UnsupportedOperationException()}

Preferences delegatesoverride fun getValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name}

Preferences delegatesoverride fun getValue(thisRef: Any, property: KProperty<*>): T { val key = name ?: property.name if (!preferences.contains(key)) { return default }}

Preferences delegatesoverride 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 delegatesoverride 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 delegatesoverride 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 delegatesoverride 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 delegatesoverride 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 delegatesinline fun <reified T : Any> bindPreference( preferences: SharedPreferences, default: T, name: String? = null) : ReadWriteProperty<Any, T> { return PreferenceReadWriteProperty(preferences, T::class.java, default, name)}

Preferences delegatesclass DebugSettings(private val preferences: SharedPreferences) { var logEnabled by bindPreference(preferences, true) var logTag by bindPreference(preferences, "Debug")}