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

Kotlin Everywhere Coimbra: Refactoring Legacy Code with Kotlin

Kotlin Everywhere Coimbra: Refactoring Legacy Code with Kotlin

Legacy code can be quite the challenge to manage, often resulting from untested scenarios, quick fixes, or less than successful initiatives. With few developers wanting to deal with it, it can end up with little remaining knowledge of its inner workings.

We can take many learnings from Michael Feathers book on "Working Effectively with Legacy Code", but we can also use Kotlin migration as an effective tool to leverage the management, reduction, and removal of legacy code in our applications.

In this session, you'll learn how to get started with Kotlin in your projects, tips and tricks on how to preserve your version control history, some pitfalls when migrating from Java, and what new technologies you can make use of in your journey with Kotlin.

Ash Davies

October 01, 2019
Tweet

More Decks by Ash Davies

Other Decks in Programming

Transcript

  1. Refactoring Legacy Code with Kotlin
    Kotlin Everywhere: Coimbra
    !
    @askashdavies | sli.do/Z169

    View Slide

  2. Legacy
    adj.
    Denoting or relating to so-ware or hardware that has been superseded
    but is di4cult to replace because of its wide use.
    @askashdavies | sli.do/Z169

    View Slide

  3. Working Effectively
    with Legacy Code
    Michael Feathers
    » Untested code
    » Regression tests
    » Lack of con3dence
    @askashdavies | sli.do/Z169

    View Slide

  4. !
    @askashdavies | sli.do/Z169

    View Slide

  5. Java
    @askashdavies | sli.do/Z169

    View Slide

  6. JVM != ART
    @askashdavies | sli.do/Z169

    View Slide

  7. twitter.com/QuinnyPig/status/1174434046853599232

    View Slide

  8. Java 8
    » Lambda expressions
    » Method references
    » Default inte8aces
    » try-with-resources
    @askashdavies | sli.do/Z169

    View Slide

  9. Streams ↝
    @askashdavies | sli.do/Z169

    View Slide

  10. @askashdavies | sli.do/Z169

    View Slide

  11. Kotlin
    @askashdavies | sli.do/Z169

    View Slide

  12. Null
    !
    @askashdavies | sli.do/Z169

    View Slide

  13. if (foo == null) {
    bar();
    }
    @askashdavies | sli.do/Z169

    View Slide

  14. Defensive Code < >
    @askashdavies | sli.do/Z169

    View Slide

  15. Offensive Code < >
    @askashdavies | sli.do/Z169

    View Slide

  16. Urgency
    @askashdavies | sli.do/Z169

    View Slide

  17. Kotlin?
    @askashdavies | sli.do/Z169

    View Slide

  18. Kotlin
    @askashdavies | sli.do/Z169

    View Slide

  19. @askashdavies | sli.do/Z169

    View Slide

  20. Google IO 2018
    @askashdavies | sli.do/Z169

    View Slide

  21. ! "
    @askashdavies | sli.do/Z169

    View Slide

  22. Google IO 2019
    @askashdavies | sli.do/Z169

    View Slide

  23. Kotlin
    @askashdavies | sli.do/Z169

    View Slide

  24. Libraries
    @askashdavies | sli.do/Z169

    View Slide

  25. @askashdavies | sli.do/Z169

    View Slide

  26. @askashdavies | sli.do/Z169

    View Slide

  27. @askashdavies | sli.do/Z169

    View Slide

  28. @askashdavies | sli.do/Z169

    View Slide

  29. @askashdavies | sli.do/Z169

    View Slide

  30. Rick and Morty, Adult Swim

    View Slide

  31. Kotlin
    @askashdavies | sli.do/Z169

    View Slide

  32. Idiomacy
    @askashdavies | sli.do/Z169

    View Slide

  33. @askashdavies | sli.do/Z169

    View Slide

  34. Idiomatic
    adj.
    Using, containing, or denoting expressions that are natural to a native
    speaker
    @askashdavies | sli.do/Z169

    View Slide

  35. Idiomatic use of Language Features
    kotlinlang.org/docs/reference/coding-conventions.html#idiomatic-use-of-language-features

    View Slide

  36. Idiomatic Code
    » Consistent, easier to read
    » Less cognitive load
    » Less ambiguity
    » Function > Style
    @askashdavies | sli.do/Z169

    View Slide

  37. Code Style
    » kotlinlang.org/docs/reference/coding-conventions.html
    » android.github.io/kotlin-guides/style.html
    @askashdavies | sli.do/Z169

    View Slide

  38. Refactoring Legacy Code
    @askashdavies | sli.do/Z169

    View Slide

  39. ⌃⌥⇧K
    @askashdavies | sli.do/Z169

    View Slide

  40. View Slide

  41. View Slide

  42. View Slide

  43. public class BadJavaActivity extends Activity {
    @Inject Dependency dependency;
    }
    @askashdavies | sli.do/Z169

    View Slide

  44. General Assumptions
    How I Met Your Mother, CBS

    View Slide

  45. class BadKotlinActivity : Activity() {
    @Inject var dependency: Dependency? = null
    }
    @askashdavies | sli.do/Z169

    View Slide

  46. class SlightlyLessBadKotlinActivity : Activity() {
    @Inject internal lateinit var dependency: Dependency
    }
    @askashdavies | sli.do/Z169

    View Slide

  47. UninitializedPropertyAccessException!
    @askashdavies | sli.do/Z169

    View Slide

  48. lateinit var file: File
    if (::file.isInitialized) { /* ... */ }
    @askashdavies | sli.do/Z169

    View Slide

  49. Nullability
    @askashdavies | sli.do/Z169

    View Slide

  50. !!
    @askashdavies | sli.do/Z169

    View Slide

  51. // Throws NullPointerException
    myNullVal.myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  52. // Still throws NullPointerException
    myNullVal!!.myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  53. // Does nothing
    !
    myNullVal?.myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  54. // Still does nothing
    !
    myNullVal?.also {
    it.myMethod()
    }
    @askashdavies | sli.do/Z169

    View Slide

  55. // Defensive Code <
    !
    >
    myNullVal?.also {
    it.myMethod()
    }
    @askashdavies | sli.do/Z169

    View Slide

  56. // Offensive Code <
    !
    >
    myNullVal!!.myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  57. // throws IllegalArgumentException("Required value was null.")
    requireNotNull(myNullVal).myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  58. // throws IllegalArgumentException("myNullVaL was null because ...")
    requireNotNull(myNullVal) { "myNullVaL was null because ..." }.myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  59. class ImpossibleException : IllegalArgumentException()
    // throws ImpossibleException()
    val myNotNullVal = myNullVall ?: throw ImpossibleException()
    myNotNullVal.myMethod()
    @askashdavies | sli.do/Z169

    View Slide

  60. class ImpossibleException : IllegalArgumentException("""
    If you're seeing this, the code is in what I thought was an unreachable state.
    I could give you advice for what to do, but honestly, why should you trust me?
    I clearly screwed this up. I'm writing a message that should never appear,
    yet I know it will probably appear someday.
    On a deep level, I know I'm not up to this task.
    I'm so sorry.
    """)
    // throws ImpossibleException()
    val myNotNullVal = myNullVall ?: throw ImpossibleException()
    myNotNullVal.myMethod()
    xkcd.com/2200/

    View Slide

  61. Data Classes
    @askashdavies | sli.do/Z169

    View Slide

  62. public class User {
    private String firstName;
    private String lastName;
    public User(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    public String getFirstName() {
    return firstName;
    }
    public void setFirstName(String firstName) {
    this.firstName = firstName;
    }
    public String getLastName() {
    return lastName;
    }
    public void setLastName(String lastName) {
    this.lastName = lastName;
    }
    @Override
    public boolean equals(Object o) {
    if (this == o) {
    return true;
    }
    if (o == null || getClass() != o.getClass()) {
    return false;
    }
    User user = (User) o;
    return Objects.equals(firstName, user.firstName) &&
    Objects.equals(lastName, user.lastName);
    }
    @Override
    public int hashCode() {
    return Objects.hash(firstName, lastName);
    }
    }
    @askashdavies | sli.do/Z169

    View Slide

  63. class User(var firstName: String?, var lastName: String?) {
    override fun equals(o: Any?): Boolean {
    if (this === o) {
    return true
    }
    if (o == null || javaClass != o.javaClass) {
    return false
    }
    val user = o as User?
    return firstName == user!!.firstName && lastName == user.lastName
    }
    override fun hashCode(): Int {
    return Objects.hash(firstName, lastName)
    }
    }
    @askashdavies | sli.do/Z169

    View Slide

  64. data class User(
    val firstName: String,
    val lastName: String
    )
    @askashdavies | sli.do/Z169

    View Slide

  65. data class User(
    val firstName: String,
    val lastName: String? = null
    )
    @askashdavies | sli.do/Z169

    View Slide

  66. @NotNull
    public final User copy(@Nullable String firstName, @Nullable String lastName) {
    return new User(firstName, lastName);
    }
    @askashdavies | sli.do/Z169

    View Slide

  67. Kotlin: Idioms
    » Singleton objects
    » String interpolation
    » Elvis operator
    !
    » Destructuring
    » Extension functions
    » Scoping functions
    kotlinlang.org/docs/reference/idioms.html

    View Slide

  68. Maintaining History
    @askashdavies | sli.do/Z169

    View Slide

  69. Maintaining History
    » Change extension .java -> .kt
    » First commit
    » Apply Kotlin conversion
    » Second commit
    @askashdavies | sli.do/Z169

    View Slide

  70. Maintaining History
    Refactoring
    @askashdavies | sli.do/Z169

    View Slide

  71. Refactoring
    @askashdavies | sli.do/Z169

    View Slide

  72. Refactoring
    SOLID
    » Single-responsibility
    » Open-closed
    » Liskov substitution
    » Inte9ace segregation
    » Dependency inversion
    @askashdavies | sli.do/Z169

    View Slide

  73. @askashdavies | sli.do/Z169

    View Slide

  74. Refactoring
    Single Responsibility
    @askashdavies | sli.do/Z169

    View Slide

  75. Perspective
    @askashdavies | sli.do/Z169

    View Slide

  76. Static
    @askashdavies | sli.do/Z169

    View Slide

  77. Asynchronicity
    @askashdavies | sli.do/Z169

    View Slide

  78. Concurrency is Hard
    @askashdavies | sli.do/Z169

    View Slide

  79. Thread
    new Thread(() -> {
    foo();
    }).start();
    @askashdavies | sli.do/Z169

    View Slide

  80. CompletableFuture
    CompletableFuture.supplyAsync(this::findSomeData)
    .thenApply(this:: intReturningMethod)
    .thenAccept(this::notify);
    @askashdavies | sli.do/Z169

    View Slide

  81. RxJava
    Observable
    .fromIterable(resourceDraft.getResources())
    .flatMap(resourceServiceApiClient::createUploadContainer)
    .zipWith(Observable.fromIterable(resourceDraft.getResources()), Pair::create)
    .flatMap(uploadResources())
    .toList()
    .toObservable()
    .flatMapMaybe(resourceCache.getResourceCachedItem())
    .defaultIfEmpty(Resource.getDefaultItem())
    .flatMap(postResource(resourceId, resourceDraft.getText(), currentUser, resourceDraft.getIntent()))
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe(
    resource -> repository.setResource(resourceId, resource, provisionalResourceId),
    resourceUploadError(resourceId, resourceDraft, provisionalResourceId)
    );
    @askashdavies | sli.do/Z169

    View Slide

  82. @askashdavies | sli.do/Z169

    View Slide

  83. What If?
    @askashdavies | sli.do/Z169

    View Slide

  84. Coroutines
    @askashdavies | sli.do/Z169

    View Slide

  85. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies | sli.do/Z169

    View Slide

  86. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies | sli.do/Z169

    View Slide

  87. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies | sli.do/Z169

    View Slide

  88. Stability
    @askashdavies | sli.do/Z169

    View Slide

  89. Annotations
    @Experimental
    @askashdavies | sli.do/Z169

    View Slide

  90. Building SDKs - The Kotlin Way
    Kotlin Everywhere: Hamburg - Jossi Wolf
    speakerdeck.com/jossiwolf/building-sdks-the-kotlin-way-ccb4a237-b45c-42e7-8391-640dd058f50c

    View Slide

  91. !
    Coroutines
    @askashdavies | sli.do/Z169

    View Slide

  92. !
    Native library
    @askashdavies | sli.do/Z169

    View Slide

  93. Efficient
    @askashdavies | sli.do/Z169

    View Slide

  94. !
    Easy-to-use
    @askashdavies | sli.do/Z169

    View Slide

  95. !
    suspend fun
    @askashdavies | sli.do/Z169

    View Slide

  96. !
    Suspend
    fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies | sli.do/Z169

    View Slide

  97. !
    Suspend
    fun main() {
    GlobalScope.launch {
    doWorld()
    println("Hello,")
    }
    Thread.sleep(2000L)
    }
    suspend fun doWorld() {
    delay(1000L)
    println("World!")
    }
    // Hello,
    // World!
    @askashdavies | sli.do/Z169

    View Slide

  98. !
    Suspend
    fun main() {
    GlobalScope.launch {
    launch { doWorld() }
    println("Hello,")
    }
    Thread.sleep(2000L)
    }
    suspend fun doWorld() {
    withContext(Dispatchers.IO) {
    delay(1000L)
    println("World!")
    }
    }
    // Hello,
    // World!
    @askashdavies | sli.do/Z169

    View Slide

  99. Testing
    @Test
    fun testFoo() = runBlockingTest {
    val actual = foo()
    // ...
    }
    suspend fun foo() {
    delay(1_000)
    // ...
    }
    @askashdavies | sli.do/Z169

    View Slide

  100. Further Reading
    !
    » Google Codelab: Refactoring to Kotlin
    codelabs.developers.google.com/codelabs/java-to-kotlin/
    » KotlinX Coroutine Test
    github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test
    » Sean McQuillan: Coroutines + Testing =
    droidcon.com/media-detail?video=352671106
    » Ash Davies: RxJava & Coroutines: A Practical Analysis v3
    speakerdeck.com/ashdavies/rxjava-and-coroutines-a-practical-analysis-v3
    @askashdavies | sli.do/Z169

    View Slide

  101. Thanks!
    @askashdavies | sli.do/Z169

    View Slide

  102. Questions
    sli.do/Z169

    View Slide