Slide 1

Slide 1 text

Kotlin: MAX PAYNE Artur Dryomov, Juno Lab @arturdryomov

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Juno Kotlin Kotlin Juno

Slide 4

Slide 4 text

Juno Rider 3 years 100 % Kotlin Kotlin EAP in production 1.1-EAP — 6 months 1.2-EAP — 3 months 99.9 % crash‑free 2 week sprints 4 developers, 1 QA

Slide 5

Slide 5 text

Juno Rider cloc main/kotlin — 56 K cloc test/kotlin — 49 K cloc androidTest/kotlin — 17 K

Slide 6

Slide 6 text

Style

Slide 7

Slide 7 text

Java Tools Nope

Slide 8

Slide 8 text

Specifications Java AST (Abstract Syntax Tree) PSI (Program Structure Interface) UAST (Universal AST)

Slide 9

Slide 9 text

Checkstyle Checkstyle works only with AST as we depend on ANTLR and Java grammar. https://github.com/checkstyle/checkstyle/issues/4369

Slide 10

Slide 10 text

Kotlin Tools IntelliJ IDEA detekt + ktlint android/lint kotlinc --force tasks.withType { kotlinOptions { allWarningsAsErrors = true } }

Slide 11

Slide 11 text

Infinity War val gameBuilder = StringBuilder() gameBuilder.append("tic") gameBuilder.append("tac") gameBuilder.append("toe") val game = gameBuilder.toString()

Slide 12

Slide 12 text

Infinity War val game = StringBuilder().also { it.append("tic") it.append("tac") it.append("toe") } .toString()

Slide 13

Slide 13 text

Infinity War val game = StringBuilder().let { it.append("tic") it.append("tac") it.append("toe") it.toString() }

Slide 14

Slide 14 text

Infinity War val game = StringBuilder().apply { append("tic") append("tac") append("toe") } .toString()

Slide 15

Slide 15 text

Infinity War val game = StringBuilder().run { append("tic") append("tac") append("toe") toString() }

Slide 16

Slide 16 text

Infinity War val game = with(StringBuilder()) { append("tic") append("tac") append("toe") toString() }

Slide 17

Slide 17 text

Infinity War ? CODE_STYLE.md

Slide 18

Slide 18 text

Weird Parts

Slide 19

Slide 19 text

Destructuring val (first, second) = Pair(1, 2) val (first, second, third) = Triple(1, 2, 3) listOf(1 to "one", 2 to "two").map { (number, text) -> println("number is $number, text is $text") }

Slide 20

Slide 20 text

Destructuring data class Person(val firstName: String, val lastName: String) val (firstName, lastName) = Person("f", "l") data class Person(val lastName: String, val firstName: String) val (firstName, lastName) = Person("f", "l") // It works!

Slide 21

Slide 21 text

Destructuring data class Person(val firstName: String, val lastName: String) { operator fun component1() = firstName operator fun component2() = lastName }

Slide 22

Slide 22 text

Destructuring Pair , Triple data class

Slide 23

Slide 23 text

null safety? Observable.just(null) public static Observable just(T item) { ObjectHelper.requireNonNull(item, "The item is null"); ...

Slide 24

Slide 24 text

null safety!! @Nullable + @NonNull

Slide 25

Slide 25 text

null safety!! Docs. Annotations. Abstractions.

Slide 26

Slide 26 text

RxJava 2 val o: Observable = Observable.just(null) // Runtime: NullPointerException public static Observable just(T item) { ObjectHelper.requireNonNull(item, "The item is null"); ...

Slide 27

Slide 27 text

Optional Swift: ? , nil enum Optional Kotlin: ? , null null

Slide 28

Slide 28 text

Koptional https://github.com/gojuno/koptional val some = Some(value) val none = None val optional = nullableValue.toOptional() val nullable = o.toNullable()

Slide 29

Slide 29 text

Mockito interface Api { fun call(id: String) } class Service(private val api: Api) { fun call() = api.call(generateId()) } @Test fun test() { val api = Mockito.mock() Service(api).call() verify(api).call(Mockito.any()) } // Runtime: NullPointerException

Slide 30

Slide 30 text

Mockito class Mockito { public static T any() { return null; } }

Slide 31

Slide 31 text

Mockito fun anything(): T = Mockito.any() @Test fun test() { val api = Mockito.mock() Service(api).call() verify(api).call(anything()) }

Slide 32

Slide 32 text

Mockito fun eq(value: T): T = Mockito.eq(obj) fun anything(): T = Mockito.any() fun whenever(call: T) = Mockito.`when`(call)

Slide 33

Slide 33 text

Gson data class Model { @SerializedName("model_field") val field: String } val model = Gson().fromJson("{}", Model::javaClass) // Runtime: OK model.field.toString() // Runtime: NullPointerException

Slide 34

Slide 34 text

Gson Java Reflection

Slide 35

Slide 35 text

Gson data class Model { @SerializedName("model_field") val field: String } : Validatable { override fun validate() { if (field == null) throw ParseException() } }

Slide 36

Slide 36 text

Gson Moshi vs. Jackson Kotlin Reflection (2.5 MiB)

Slide 37

Slide 37 text

null safety!! Kotlin: The Problem with null https://arturdryomov.online/posts/kotlin‑the‑problem‑with‑null/

Slide 38

Slide 38 text

Optimizations

Slide 39

Slide 39 text

inline private fun createId(): Int = 42 vs. @Suppress("nothing_to_inline") private inline fun createId(): Int = 42

Slide 40

Slide 40 text

inline https://youtrack.jetbrains.com/issue/KT‑16769 Revisit compiler and IDE warning about inline functions

Slide 41

Slide 41 text

const companion object { private val NUMBER = 42 } public static final class Companion { private final int NUMBER = 42; private final int getNUMBER() { return NUMBER; } }

Slide 42

Slide 42 text

const companion object { private const val NUMBER = 42 } public static final class Companion { private static final int NUMBER = 42; }

Slide 43

Slide 43 text

const Primitives + String kotlinc ?

Slide 44

Slide 44 text

Tools Kotlin Show Kotlin Bytecode

Slide 45

Slide 45 text

Tooling

Slide 46

Slide 46 text

Bugs fun multiply(double: Double) = double * double 1.1.3 org.jetbrains.kotlin.kapt3.diagnostic.KaptError Error while annotation processing

Slide 47

Slide 47 text

Bugs https://youtrack.jetbrains.com/issue/KT‑18377 Syntax error while generating kapt stubs

Slide 48

Slide 48 text

Bugs Are a thing!

Slide 49

Slide 49 text

Assemble javac kotlinc kapt

Slide 50

Slide 50 text

kapt Kotlin Annotation Processing Tool Breaks incremental compilation. Throws errors. *_MemberInjector.java: error: package does not exist

Slide 51

Slide 51 text

kapt A Dagger to Remember https://arturdryomov.online/posts/a‑dagger‑to‑remember/

Slide 52

Slide 52 text

Assemble — i5-5257U @ 2.70GHz — i7-6700K @ 4.00GHz

Slide 53

Slide 53 text

Mainframer https://github.com/gojuno/mainframer $ rsync laptop mainframe $ make $ rsync mainframe laptop

Slide 54

Slide 54 text

Mainframer $ ./gradlew assembleDebug 4 minutes 10 seconds $ mainframer ./gradlew assembleDebug 1 minute 10 seconds

Slide 55

Slide 55 text

Assemble Remove everything possible. Gradle build cache. Mainframer.

Slide 56

Slide 56 text

Gradle Kotlin DSL

Slide 57

Slide 57 text

Kotlin versions Gradle 4.2 — 1.1.4‑3 Project — 1.2.0‑beta‑31 Runtime files in the classpath should have the same version. These files were found in the classpath: .../dists/.../kotlin-stdlib-1.1.4-3.jar (version 1.1) .../caches/.../kotlin-stdlib-1.2.0-beta-31.jar (version 1.2)

Slide 58

Slide 58 text

Kotlin versions https://github.com/gradle/kotlin‑dsl/issues/519

Slide 59

Slide 59 text

Groovy unitTests.all { maxParallelForks = 2 } unitTests.all(KotlinClosure1({ apply { maxParallelForks = 2 } }, this))

Slide 60

Slide 60 text

Groovy import groovy.lang.Closure; public void unitTests(Closure closure) https://issuetracker.google.com/issues/78547461

Slide 61

Slide 61 text

Groovy 2012: groovy.lang.Closure 2017: org.gradle.api.Action

Slide 62

Slide 62 text

Groovy Promote DSL‑agnostic API

Slide 63

Slide 63 text

Gradle Kotlin DSL IDE Dynamic Groovy Workflow Alpha!

Slide 64

Slide 64 text

Spek

Slide 65

Slide 65 text

JUnit class SystemTest { lateinit var system: System @BeforeEach fun create() { system = System() } @BeforeEach fun start() { system.boot() } @Test fun `has boot status`() { assertThat(system.status()).isEqualTo(BOOTED) } @Test fun `has positive uptime`() { assertThat(system.uptime()).isNotZero() } }

Slide 66

Slide 66 text

Spek class SystemSpec : Spek({ val system by memoized { System() } context("boot") { beforeEachTest { system.boot() } it("has boot status") { assertThat(system.status()).isEqualTo(BOOTED) } it("has positive uptime") { assertThat(system.uptime()).isNotZero() } } })

Slide 67

Slide 67 text

Versions 1.0.89 : DescribeBody Dsl 1.1.0-beta3 : Dsl SpecBody 1.0 , 1.1.19 : do not exist. JUnit 4 5

Slide 68

Slide 68 text

memoized before and after class JustSpec : Spek({ val system by memoized { System() } beforeEachTest { println("before $system") // system_before } afterEachTest { println("after $system") // system_after } }) // system_before != system_after

Slide 69

Slide 69 text

memoized before and after https://github.com/JetBrains/spek/issues/213 Memoized value is not the same before and after test

Slide 70

Slide 70 text

JUnit JUnit 5! Android Studio JUnit 4? @RunWith(JUnitPlatform::class) fon , fit Spek IJ plugin. JUnit 4 Fun

Slide 71

Slide 71 text

Spek jetbrains/spek spekframework/spek Compatibility Docs Bugs 100 K tests 2.0

Slide 72

Slide 72 text

Spek Spectrum, KotlinTest

Slide 73

Slide 73 text

Conclusion

Slide 74

Slide 74 text

https://youtrack.jetbrains.com/issues/KT

Slide 75

Slide 75 text

... Kotlin is a thin layer on top of Java, and so it perpetuates through a lot of the Javaisms in its model. ... Chris Lattner (Swift, LLVM, Clang)

Slide 76

Slide 76 text

Java — Executive Committee Oracle, Red Hat, Intel, IBM, Eclipse, Twitter, JetBrains... 24 members Kotlin — Language Committee JetBrains, Google 3 members

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

Conclusion JVM Tooling Language

Slide 79

Slide 79 text

No content