Slide 1

Slide 1 text

Refactoring Legacy Code with Kotlin & Coroutines Kotlin Everywhere: Hamburg @askashdavies

Slide 2

Slide 2 text

@askashdavies

Slide 3

Slide 3 text

Java @askashdavies

Slide 4

Slide 4 text

{ } @askashdavies

Slide 5

Slide 5 text

Streams ↝ @askashdavies

Slide 6

Slide 6 text

@askashdavies

Slide 7

Slide 7 text

Kotlin @askashdavies

Slide 8

Slide 8 text

Null @askashdavies

Slide 9

Slide 9 text

if (foo == null) { bar(); } @askashdavies

Slide 10

Slide 10 text

O"ensive Code < > @askashdavies

Slide 11

Slide 11 text

Urgency @askashdavies

Slide 12

Slide 12 text

Kotlin? @askashdavies

Slide 13

Slide 13 text

Kotlin @askashdavies

Slide 14

Slide 14 text

@askashdavies

Slide 15

Slide 15 text

Google IO 2018 @askashdavies

Slide 16

Slide 16 text

! " @askashdavies

Slide 17

Slide 17 text

Google IO 2019 @askashdavies

Slide 18

Slide 18 text

Kotlin @askashdavies

Slide 19

Slide 19 text

Libraries @askashdavies

Slide 20

Slide 20 text

@askashdavies

Slide 21

Slide 21 text

@askashdavies

Slide 22

Slide 22 text

@askashdavies

Slide 23

Slide 23 text

@askashdavies

Slide 24

Slide 24 text

@askashdavies

Slide 25

Slide 25 text

@askashdavies

Slide 26

Slide 26 text

Kotlin @askashdavies

Slide 27

Slide 27 text

Idiomacy @askashdavies

Slide 28

Slide 28 text

@askashdavies

Slide 29

Slide 29 text

Idiomatic Code @askashdavies

Slide 30

Slide 30 text

Idiomatic Code • Consistent, easier to read • Less cognitive load • Less ambiguity • Function > Style @askashdavies

Slide 31

Slide 31 text

Code Style • kotlinlang.org/docs/reference/coding-conventions.html • android.github.io/kotlin-guides/style.html @askashdavies

Slide 32

Slide 32 text

Refactoring Legacy Code @askashdavies

Slide 33

Slide 33 text

⌃⌥⇧K @askashdavies

Slide 34

Slide 34 text

@askashdavies

Slide 35

Slide 35 text

@askashdavies

Slide 36

Slide 36 text

public class BadJavaActivity extends Activity { @Inject Dependency dependency; } @askashdavies

Slide 37

Slide 37 text

General Assumptions @askashdavies

Slide 38

Slide 38 text

class BadKotlinActivity : Activity() { @Inject var dependency: Dependency? = null } @askashdavies

Slide 39

Slide 39 text

class SlightlyLessBadKotlinActivity : Activity() { @Inject internal lateinit var dependency: Dependency } @askashdavies

Slide 40

Slide 40 text

UninitializedPrope.yAccessException! @askashdavies

Slide 41

Slide 41 text

lateinit var file: File if (::file.isInitialized) { /* ... */ } @askashdavies

Slide 42

Slide 42 text

Data Classes @askashdavies

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

data class User(val firstName: String, val lastName: String) @askashdavies

Slide 46

Slide 46 text

data class User(val firstName: String, val lastName: String? = null) @askashdavies

Slide 47

Slide 47 text

@NotNull public final User copy(@Nullable String firstName, @Nullable String lastName) { return new User(firstName, lastName); } @askashdavies

Slide 48

Slide 48 text

Kotlin • Singleton objects • String interpolation • Elvis operator ! • Destructuring • Extension functions • Scoping functions @askashdavies

Slide 49

Slide 49 text

Maintaining History @askashdavies

Slide 50

Slide 50 text

Maintaining History • Change extension .java -> .kt • First commit • Apply Kotlin conversion • Second commit @askashdavies

Slide 51

Slide 51 text

Maintaining History Refactoring @askashdavies

Slide 52

Slide 52 text

Refactoring @askashdavies

Slide 53

Slide 53 text

Refactoring SOLID • Single-responsibility • Open-closed • Liskov substitution • Interface segregation • Dependency inversion @askashdavies

Slide 54

Slide 54 text

Refactoring Single Responsibility @askashdavies

Slide 55

Slide 55 text

Perspective @askashdavies

Slide 56

Slide 56 text

Asynchronicity @askashdavies

Slide 57

Slide 57 text

Concurrency is Hard @askashdavies

Slide 58

Slide 58 text

Thread new Thread(() -> { foo(); }).start(); @askashdavies

Slide 59

Slide 59 text

CompletableFuture CompletableFuture.supplyAsync(this::findSomeData) .thenApply(this:: intReturningMethod) .thenAccept(this::notify); @askashdavies

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

@askashdavies

Slide 62

Slide 62 text

What If? @askashdavies

Slide 63

Slide 63 text

Coroutines @askashdavies

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

⚖ Stability @askashdavies

Slide 68

Slide 68 text

@askashdavies

Slide 69

Slide 69 text

@Annotations ( ! Here be dragons) @askashdavies

Slide 70

Slide 70 text

Annotations @ExperimentalCoroutinesApi // ⚠ @askashdavies

Slide 71

Slide 71 text

Annotations @ExperimentalCoroutinesApi // ⚠ @FlowPreview // ⚠ @askashdavies

Slide 72

Slide 72 text

Annotations @ExperimentalCoroutinesApi // ⚠ @FlowPreview // ⚠ @ObsoleteCoroutinesApi // ⚠ @askashdavies

Slide 73

Slide 73 text

Annotations @ExperimentalCoroutinesApi // ⚠ @FlowPreview // ⚠ @ObsoleteCoroutinesApi // ⚠ @InternalCoroutinesApi // ☠ @askashdavies

Slide 74

Slide 74 text

Annotations @Experimental @askashdavies

Slide 75

Slide 75 text

! Coroutines @askashdavies

Slide 76

Slide 76 text

! Native (rst-pa-y library @askashdavies

Slide 77

Slide 77 text

E"cient @askashdavies

Slide 78

Slide 78 text

! Easy-to-use @askashdavies

Slide 79

Slide 79 text

! suspend fun @askashdavies

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Dispatchers @askashdavies

Slide 84

Slide 84 text

Dispatchers • Default • IO • Main • Android (Main Thread Dispatcher) • JavaFx (Application Thread Dispatcher) • Swing (Event Dispatcher Thread) • Unconfined @askashdavies

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

Fu#her Reading • Google Codelab: Refactoring to Kotlin https://codelabs.developers.google.com/codelabs/java-to-kotlin/ • KotlinX Coroutine Test https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test • Sean McQuillan: Coroutines + Testing = https://www.droidcon.com/media-detail?video=352671106 • Ash Davies: RxJava & Coroutines: A Practical Analysis v3 https://speakerdeck.com/ashdavies/rxjava-and-coroutines-a-practical-analysis- v3 @askashdavies

Slide 87

Slide 87 text

Thanks! @askashdavies