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

Kotlin Experience. Hiding the complexity

Kotlin Experience. Hiding the complexity

In this talk, I cover my practical experience with Kotlin.
Some examples of using new language features to hide the complexity, along with helpful tools to improve the development process.
Also, I gave main starting points for complete beginners, as well as for people feeling not so confident with Kotlin.

Avatar for Dmitrii Soldatov

Dmitrii Soldatov

February 04, 2019

Other Decks in Programming

Transcript

  1. Kotlin timeline «With the release of M6, we announced support

    for Android Studio.» JetBrains blog 2013 2016 «This is it. 1.0 is here!» 2017 «Today the Android team is excited to announce that we are officially adding support for the Kotlin programming language.» Android Developers blog 2019 «Kotlin is now an official language for Android development inside Google!» @jmslau on Twitter
  2. How to dive into Kotlin? • Kotlin Koans
 https://play.kotlinlang.org/koans •

    Kotlin Weekly
 http://www.kotlinweekly.net/ • Jake Wharton talks
 https://jakewharton.com/advancing-development-with-kotlin/ • ADB podcast, Episode 91: KTX
 http://androidbackstage.blogspot.com/2018/05/episode-91-ktx.html
  3. Transition from Java • Perfect interop in both directions •

    Idiomatic code https://kotlinlang.org/docs/reference/idioms.html • Getting under the hood. Decompile Kotlin byte code
 
 interface Validator<Field> { fun validate(field: Field): Field } 
 
 @Metadata(…) public interface Validator { Object validate(Object var1); } • New tools to deal with code complexity
  4. Parcelization class Student(val id: Long = 0, val name: String?

    = null, val grade: String? = null) : Parcelable { override fun describeContents() = 0 override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeLong(this.id) dest.writeString(this.name) dest.writeString(this.grade) } companion object { val CREATOR: Parcelable.Creator<Student?> = object : Parcelable.Creator<Student?> { override fun createFromParcel(src: Parcel): Student { return Student( src.readLong(), src.readString(), src.readString() ) } override fun newArray(size: Int) = arrayOfNulls<Student>(size) } } }
  5. Parcelization (easy) @Parcelize data class Student( val id: Long =

    0, val name: String? = null, val grade: String? = null ) : Parcelable
  6. const val EXTRA_MESSAGE = "com.example.app.MESSAGE" class MainActivity : Activity() {

    fun showOtherActivity() { val intent = Intent(this, OtherActivity::class.java).apply { putExtra(EXTRA_MESSAGE, textField.text) } startActivity(intent) } } class OtherActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textField.text = intent.getStringExtra(EXTRA_MESSAGE) } }
  7. class MainActivity : Activity() { fun showOtherActivity() { val intent

    = Intent(this, OtherActivity::class.java).apply { putExtra(EXTRA_MESSAGE, textField.text) } startActivity(intent) } } const val EXTRA_MESSAGE = "com.example.app.MESSAGE" class OtherActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textField.text = intent.getStringExtra(EXTRA_MESSAGE) } }
  8. class MainActivity : Activity() { fun showOtherActivity() { val intent

    = OtherActivity.newIntent(this, textField.text) startActivity(intent) } } class OtherActivity : Activity() { companion object { private const val EXTRA_MESSAGE = "com.example.app.MESSAGE" fun newIntent(context: Context, message: String) = Intent(context, OtherActivity::class.java).apply { putExtra(EXTRA_MESSAGE, message) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textField.text = intent.getStringExtra(EXTRA_MESSAGE) } }
  9. class OtherActivity : Activity() { companion object { private const

    val EXTRA_MESSAGE = "com.example.app.MESSAGE" fun newIntent(context: Context, message: String) = Intent(context, OtherActivity::class.java).apply { putExtra(EXTRA_MESSAGE, message) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textField.text = intent.getStringExtra(EXTRA_MESSAGE) } }
  10. class OtherActivity : Activity() { companion object { private const

    val EXTRA_MESSAGE = "com.example.app.MESSAGE" private var Intent.message: String get() = getStringExtra(EXTRA_MESSAGE) set(value) { putExtra(EXTRA_MESSAGE, value) } fun newIntent(context: Context, message: String) = Intent(context, OtherActivity::class.java).apply { this.message = message } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textField.text = intent.message } }
  11. class StringExtra { operator fun getValue(thisRef: Intent, property: KProperty<*>): String?

    { return thisRef.getStringExtra(property.name) } operator fun setValue(thisRef: Intent, property: KProperty<*>, value: String?) { thisRef.putExtra(property.name, value) } }
  12. class StringExtra { operator fun getValue(thisRef: Intent, property: KProperty<*>): String?

    { return thisRef.getStringExtra(property.name) } operator fun setValue(thisRef: Intent, property: KProperty<*>, value: String?) { thisRef.putExtra(property.name, value) } } class OtherActivity : Activity() { companion object { private var Intent.message by StringExtra() fun newIntent(context: Context, message: String) = Intent(context, OtherActivity::class.java).apply { this.message = message } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textField.text = intent.message } }
  13. class IntExtra(private val defaultValue: Int = 0) { operator fun

    getValue(thisRef: Intent, property: KProperty<*>): Int { return thisRef.getIntExtra(property.name, defaultValue) } operator fun setValue(thisRef: Intent, property: KProperty<*>, value: Int) { thisRef.putExtra(property.name, value) } } class SerializableExtra<T : Serializable> { operator fun getValue(thisRef: Intent, property: KProperty<*>): T { return thisRef.getSerializableExtra(property.name) as T } operator fun setValue(thisRef: Intent, property: KProperty<*>, value: T) { thisRef.putExtra(property.name, value as Serializable) } }
  14. Reading view attributes class CustomButton @JvmOverloads constructor( context: Context, attrs:

    AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { init { val attrIdArray = intArrayOf(android.R.attr.color) val styledAttrs = context.obtainStyledAttributes(attrs, attrIdArray) val defaultColor = ContextCompat.getColor(context, android.R.color.black) val color = styledAttrs.getColor(0, defaultColor).also { styledAttrs.recycle() } setBackgroundColor(color) } }
  15. Reading view attributes class ColorAttributeProperty( private val attrs: AttributeSet?, private

    val attrId: Int, private val defaultValue: Int ) { operator fun getValue(thisRef: View, property: KProperty<*>): Int { if (attrs == null) { return defaultValue } val attrIdArray = intArrayOf(attrId) val styledAttrs = thisRef.context.obtainStyledAttributes(attrs, attrIdArray) val result = styledAttrs.getColor(0, defaultValue) styledAttrs.recycle() return result } }
  16. Reading view attributes class CustomButton @JvmOverloads constructor( context: Context, attrs:

    AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val black by lazy { ContextCompat.getColor(context, R.color.black) } private val View.color by ColorAttributeProperty(attrs, android.R.attr.color, black) init { setBackgroundColor(color) } }
  17. Extension property delegates • Hides the routine of getting/setting values

    • Works in specific scope • Improves code readability • Can extend third party classes
  18. open class Presenter<View> { private var view: View? = null

    fun attachView(view: View) { this.view = view } fun detachView() { this.view = null } } class MainPresenter( private val authApi: AuthApi, private val postApi: PostApi ) : Presenter<View>() { fun onSubmit(post: Post) = authApi.getUser() .flatMap { user -> postApi.add(user.name, post.content) } .subscribe() }
  19. open class Presenter<View> { private var view: View? = null

    fun attachView(view: View) { this.view = view } fun detachView() { this.view = null } } class MainPresenter( private val authApi: AuthApi, private val postApi: PostApi ) : Presenter<View>() { fun onSubmit(post: Post) = authApi.getUser() .flatMap { user -> postApi.add(user.name, post.content) } .subscribe() }
  20. class MainPresenter( private val authApi: AuthApi, private val postApi: PostApi

    ) : Presenter<View>() { private var subscription: Disposable? = null fun onSubmit(post: Post) { subscription = authApi.getUser() .flatMap { user -> postApi.add(user.name, post.content) } .subscribe() } override fun detachView() { subscription?.dispose() super.detachView() } } class MainPresenter( private val authApi: AuthApi, private val postApi: PostApi ) : Presenter<View>() { private var subscription: Disposable? = null fun onSubmit(post: Post) { subscription = authApi.getUser() .flatMap { user -> postApi.add(user.name, post.content) } .subscribe() } override fun detachView() { subscription?.dispose() super.detachView() } }
  21. open class Presenter<View> { private var view: View? = null

    fun attachView(view: View) { this.view = view } fun detachView() { this.view = null } } class MainPresenter( private val authApi: AuthApi, private val postApi: PostApi ) : Presenter<View>() { fun onSubmit(post: Post) = authApi.getUser() .flatMap { user -> postApi.add(user.name, post.content) } .subscribe() }
  22. open class Presenter<View> { private var view: View? = null

    private val compositeSubscription = CompositeDisposable() open fun attachView(view: View) { this.view = view } open fun detachView() { this.view = null compositeSubscription.clear() } }
  23. open class Presenter<View> { private var view: View? = null

    protected val compositeSubscription = CompositeDisposable() open fun attachView(view: View) { this.view = view } open fun detachView() { this.view = null compositeSubscription.clear() } protected inline fun <T> Single<T>.subscribeUntilDetach( noinline onNext: (T) -> Unit) = subscribe(onNext).apply { compositeSubscription.add(this) } }
  24. Conclusion • Kotlin offers new tools • Transition is easy

    • Currently it lacks standartization • Requires team conventions • It’s worth it!