An Introduction to Kotlin

An Introduction to Kotlin

Kotlin sure has been receiving a lot of buzz lately, is there something to it? Developed by JetBrains, Kotlin has been catching on lately because of its null safety guarantees, functional nature, type inference, full interoperability with Java, cross-platform support, and ease of use with Android and Spring. Is this something you and your team should considering spending time to learn?

One of the most interesting aspects of Kotlin is its design philosophy. Bugs that exist in Java such as NullPointerExceptions are not possible in Kotlin. This mentality of preventing common bugs at the language level exists all through the Kotlin syntax and standard library. Kotlin is also far less verbose than Java, reducing the amount of code you need to read in order to understand it. It also runs on JVMs down to Java 6, without sacrificing features. So if you are in a constrained environment such as Android or an enterprise shop struggling to upgrade, Kotlin might be worth considering.

In this talk, you will learn where Kotlin came from, what its major features are, and why people are using it more and more. At the end of the talk, you will be in a position to make an educated decision about whether Kotlin is right for you!

2777430c1f9869e411ab5d8b17d8ba2a?s=128

Todd Ginsberg

April 16, 2020
Tweet

Transcript

  1. @ToddGinsberg An Introduction To Kotlin Utah Java Users Group 2020-04-16

    Todd Ginsberg Principal Software Developer
  2. @ToddGinsberg What’s in a Name? By Editors of Open Street

    Map - Open Street Map, CC
  3. @ToddGinsberg What’s in a Name? Kotlin Island By Editors of

    Open Street Map - Open Street Map, CC
  4. @ToddGinsberg Agenda - What Is Kotlin?

  5. @ToddGinsberg Agenda - What Is Kotlin? - Am I Alone?

  6. @ToddGinsberg Agenda - What Is Kotlin? - Am I Alone?

    - Syntax & Features
  7. @ToddGinsberg Agenda - What Is Kotlin? - Am I Alone?

    - Syntax & Features - Summary
  8. @ToddGinsberg Agenda - What Is Kotlin? - Am I Alone?

    - Syntax & Features - Summary - Questions and Possibly Answers
  9. @ToddGinsberg Who is this Guy? Todd Ginsberg

  10. @ToddGinsberg Who is this Guy? Todd Ginsberg Just moved to

    Raleigh from Chicago
  11. @ToddGinsberg Who is this Guy? Todd Ginsberg Just moved to

    Raleigh from Chicago Principal Developer at Netspend - A payments company in Austin, TX! - We love Java and Kotlin!
  12. @ToddGinsberg One More Thing... I

  13. @ToddGinsberg What Is Kotlin?

  14. @ToddGinsberg What Is Kotlin? Statically typed language, developed by JetBrains

  15. @ToddGinsberg What Is Kotlin? Statically typed language, developed by JetBrains

    Released under Apache 2.0 license
  16. @ToddGinsberg What Is Kotlin? Statically typed language, developed by JetBrains

    Released under Apache 2.0 license Designed as a general purpose language
  17. @ToddGinsberg What Is Kotlin? Statically typed language, developed by JetBrains

    Released under Apache 2.0 license Designed as a general purpose language • Targets JVM 6 or 8 bytecode
  18. @ToddGinsberg What Is Kotlin? Statically typed language, developed by JetBrains

    Released under Apache 2.0 license Designed as a general purpose language • Targets JVM 6 or 8 bytecode • Targets ECMAScript 5.1
  19. @ToddGinsberg What Is Kotlin? Statically typed language, developed by JetBrains

    Released under Apache 2.0 license Designed as a general purpose language • Targets JVM 6 or 8 bytecode • Targets ECMAScript 5.1 • Targets other native platforms thanks to LLVM
  20. @ToddGinsberg Major Features 100% Interoperable with Java

  21. @ToddGinsberg Major Features 100% Interoperable with Java Designed to avoid

    entire classes of defects
  22. @ToddGinsberg Major Features 100% Interoperable with Java Designed to avoid

    entire classes of defects Lots of small improvements that add up
  23. @ToddGinsberg Major Features 100% Interoperable with Java Designed to avoid

    entire classes of defects Lots of small improvements that add up Far less code to accomplish the same task in Java - Less cognitive load on developers
  24. @ToddGinsberg Major Features Null-safe

  25. @ToddGinsberg Major Features Null-safe 199,000+ issues on GitHub!

  26. @ToddGinsberg Major Features Null-safe 199,000+ issues on GitHub! 15,650 duplicates

    on Stack Overflow!
  27. @ToddGinsberg Major Features Pragmatic Improvements No shame in copying language

    features that make developers more productive
  28. @ToddGinsberg Major Features Positive Community

  29. @ToddGinsberg Major Features Positive Community Even on Stack Overflow!

  30. @ToddGinsberg Major Features Positive Community Even on Stack Overflow! RTFM!

  31. @ToddGinsberg Major Features Positive Community Even on Stack Overflow! RTFM!

  32. @ToddGinsberg Am I Alone?

  33. @ToddGinsberg Supported on Android 2017 It should work, give it

    a try!
  34. @ToddGinsberg Supported on Android 2018 Prefer Kotlin over Java

  35. @ToddGinsberg Supported on Android 2019 Android is now Kotlin first!

  36. @ToddGinsberg Spring Framework Support Kotlin is fully supported since Spring

    Framework 5
  37. @ToddGinsberg Spring Framework Support Kotlin is fully supported since Spring

    Framework 5 Kotlin is an option on start.spring.io
  38. @ToddGinsberg Spring Framework Support Kotlin is fully supported since Spring

    Framework 5 Kotlin is an option on start.spring.io Spring @NotNull annotations == Better Kotlin nullability support
  39. @ToddGinsberg Spring Framework Support Kotlin is fully supported since Spring

    Framework 5 Kotlin is an option on start.spring.io Spring @NotNull annotations == Better Kotlin nullability support Comprehensive Kotlin documentation and examples
  40. @ToddGinsberg Gradle DSL

  41. @ToddGinsberg Syntax

  42. @ToddGinsberg Variables and Values var place: String = "Salt Lake

    City"
  43. @ToddGinsberg Variables and Values var place: String = "Salt Lake

    City" place = "UT" // OK!
  44. @ToddGinsberg Variables and Values var place: String = "Salt Lake

    City" place = "UT" // OK! val name: String = "Todd"
  45. @ToddGinsberg Variables and Values var place: String = "Salt Lake

    City" place = "UT" // OK! val name: String = "Todd" name = "Emma" // Compile Error!
  46. @ToddGinsberg Type Inference val d: Int = 2 val text:

    String = "Todd has $d doughnuts"
  47. @ToddGinsberg Type Inference val d: Int = 2 val text:

    String = "Todd has $d doughnuts"
  48. @ToddGinsberg Type Inference val d = 2 val text =

    "Todd has $d doughnuts"
  49. @ToddGinsberg Equality val name1 = "EXAMPLE" val name2 = "example"

  50. @ToddGinsberg Equality val name1 = "EXAMPLE" val name2 = "example"

    // Structural Equality name1 == name2.toUpperCase() // True!
  51. @ToddGinsberg Equality val name1 = "EXAMPLE" val name2 = "example"

    // Structural Equality name1 == name2.toUpperCase() // True! // Referential Equality name1 === name2 // False!
  52. @ToddGinsberg Raw Strings val json = "{\n\"name\": \"Todd\"\n}"

  53. @ToddGinsberg Raw Strings val json = "{\n\"name\": \"Todd\"\n}" val json

    = """{ "name": "Todd" }"""
  54. @ToddGinsberg Raw Strings val json = "{\n\"name\": \"Todd\"\n}" val json

    = """ { "name": "Todd" } """
  55. @ToddGinsberg Null Safety // Guaranteed to never be null var

    name: String = "Todd"
  56. @ToddGinsberg Null Safety // Guaranteed to never be null var

    name: String = "Todd" // May be null var salary: Int? = null
  57. @ToddGinsberg Null-Safe Traversal var city: String? = "Salt Lake City"

  58. @ToddGinsberg Null-Safe Traversal var city: String? = "Salt Lake City"

    // Not allowed, might be null! city.toUpperCase()
  59. @ToddGinsberg Null-Safe Traversal var city: String? = "Salt Lake City"

    // Not allowed, might be null! city.toUpperCase() // Safe traversal city?.toUpperCase()
  60. @ToddGinsberg Elvis val lowest : Int? = listOf(1, 2, 3).min()

  61. @ToddGinsberg Elvis val lowest : Int? = listOf(1, 2, 3).min()

    val lowest : Int = listOf(1, 2, 3).min() ?: 0
  62. @ToddGinsberg Combine Safe-Traversal and Elvis println( city?.toUpperCase() ?: ”UNKNOWN” )

  63. @ToddGinsberg Manual Override val lowest: Int? = listOf(1, 2, 3).min()

  64. @ToddGinsberg Manual Override val lowest: Int? = listOf(1, 2, 3).min()

    val lowest: Int = listOf(1, 2, 3).min()!!
  65. @ToddGinsberg Manual Override val lowest: Int? = listOf(1, 2, 3).min()

    val lowest: Int = listOf(1, 2, 3).min()!! val lowest: Int = emptyList<Int>().min()!!
  66. @ToddGinsberg Manual Override val lowest: Int? = listOf(1, 2, 3).min()

    val lowest: Int = listOf(1, 2, 3).min()!! val lowest: Int = emptyList<Int>().min()!! // NullPointerException!
  67. @ToddGinsberg Null Safety Remember the names!

  68. @ToddGinsberg Null Safety Remember the names! ?. == Safe Traversal

  69. @ToddGinsberg Null Safety Remember the names! ?. == Safe Traversal

    ?: == Elvis
  70. @ToddGinsberg Null Safety Remember the names! ?. == Safe Traversal

    ?: == Elvis !! == Hold My Beer
  71. @ToddGinsberg Expressions - if val status = if (code ==

    42) { "Success" } else { "Fail" }
  72. @ToddGinsberg Expressions - try/catch val number = try { code.toInt()

    } catch (e: NumberFormatException) { 0 }
  73. @ToddGinsberg Expressions - when val result = when (x) {

    0 -> "x is 0” in 1..10 -> "x is between 1 and 10" in someSet -> "x is in someSet" is SomeType -> "x is an instance of SomeType" parseString(s) -> "the same as parseString" else -> "x doesn't match anything" }
  74. @ToddGinsberg Smart Casting when (x) { is Int -> print(x

    % 2 == 0) is String -> print(x.length + 1) is IntArray -> print(x.sum()) }
  75. @ToddGinsberg Smart Casting when (x) { is Int -> print(x

    % 2 == 0) is String -> print(x.length + 1) is IntArray -> print(x.sum()) }
  76. @ToddGinsberg Smart Casting if(x != null) { println(x.toString()) }

  77. @ToddGinsberg Classes class Entity : SomeInterface { // ... }

  78. @ToddGinsberg Classes - Inheritance open class Entity : SomeInterface {

    // ... } class Customer : Entity() { // ... }
  79. @ToddGinsberg Properties public class Customer { private String name; public

    String getName() { return name; } public void setName(final String name) { this.name = name; } }
  80. @ToddGinsberg Properties class Customer { var name: String? = null

    }
  81. @ToddGinsberg Properties class Customer { var name: String? = null

    } val c = Customer()
  82. @ToddGinsberg Properties class Customer { var name: String? = null

    } val c = Customer() c.name = "Todd"
  83. @ToddGinsberg Properties class Customer { var name: String? = null

    } val c = Customer() c.name = "Todd" println("My name is ${c.name}")
  84. @ToddGinsberg Properties class Customer { var name: String? = null

    }
  85. @ToddGinsberg Properties class Customer { var name: String? = null

    set(value) { field = value?.toUpperCase() } }
  86. @ToddGinsberg Properties class Customer { var name: String? = null

    private set(value) { field = value?.toUpperCase() } }
  87. @ToddGinsberg Properties class Customer { var name: String? = null

    get() { return field?.toUpperCase() } }
  88. @ToddGinsberg Property Delegation class Country { val gdp: Int =

    slowCalculation() }
  89. @ToddGinsberg Property Delegation class Country { val gdp: Int by

    lazy { slowCalculation() } }
  90. @ToddGinsberg Class Delegation // Java class MyJavaImpl implements HugeInterface {

    private final HugeInterface backing; public MyJavaImpl(HugeInterface hi) { backing = hi; } // IMPLEMENT EVERYTHING }
  91. @ToddGinsberg Class Delegation // Java class MyJavaImpl extends SomeImplementation {

    // Implement Some }
  92. @ToddGinsberg Class Delegation // Kotlin class MyImpl(h: HugeInterface) : HugeInterface

    by h { // Implement Some }
  93. @ToddGinsberg Sealed Classes sealed class Message class StartServer(val name: String)

    : Message() class StopServer(val name: String) : Message() object CountServers : Message()
  94. @ToddGinsberg Sealed Classes val response = when(msg) { is StartServer

    -> startServer(msg.name) is StopServer -> stopServer(msg.name) is CountServers -> countServers() }
  95. @ToddGinsberg Let’s Write a POJO! public class Person { public

    String firstName; public String lastName; }
  96. @ToddGinsberg Let’s Write a POJO! public class Person { private

    String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } }
  97. @ToddGinsberg Let’s Write a POJO! public class Person { private

    String firstName; private String lastName; public Person() { } public Person(final String firstName, final String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } }
  98. @ToddGinsberg Let’s Write a POJO! import java.util.Objects; public class Person

    { private String firstName; private String lastName; public Person() { } public Person(final String firstName, final String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Person person = (Person) o; return Objects.equals(firstName, person.firstName) && Objects.equals(lastName, person.lastName); } @Override public int hashCode() { return Objects.hash(firstName, lastName); } }
  99. @ToddGinsberg Let’s Write a POJO! import java.util.Objects; public class Person

    { private String firstName; private String lastName; public Person() { } public Person(final String firstName, final String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Person person = (Person) o; return Objects.equals(firstName, person.firstName) && Objects.equals(lastName, person.lastName); } @Override public int hashCode() { return Objects.hash(firstName, lastName); } @Override public String toString() { return "Person{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + '}'; } }
  100. @ToddGinsberg Data Classes to the Rescue! data class Person(val firstName:

    String, val lastName: String)
  101. @ToddGinsberg Data Classes to the Rescue! data class Person(val firstName:

    String, val lastName: String) • Getters (and Setters for vars) as Properties
  102. @ToddGinsberg Data Classes to the Rescue! data class Person(val firstName:

    String, val lastName: String) • Getters (and Setters for vars) as Properties • toString()
  103. @ToddGinsberg Data Classes to the Rescue! data class Person(val firstName:

    String, val lastName: String) • Getters (and Setters for vars) as Properties • toString() • hashCode() and equals()
  104. @ToddGinsberg Data Classes to the Rescue! data class Person(val firstName:

    String, val lastName: String) • Getters (and Setters for vars) as Properties • toString() • hashCode() and equals() • And…
  105. @ToddGinsberg Copying Data Classes val me = Person("Todd", "Ginsberg")

  106. @ToddGinsberg Copying Data Classes val me = Person("Todd", "Ginsberg") val

    emma = me.copy(firstName = "Emma") // Person(”Emma", "Ginsberg")
  107. @ToddGinsberg Destructuring Data Classes val me = Person("Todd", "Ginsberg") val

    (first, last) = me // first == “Todd” last == “Ginsberg”
  108. @ToddGinsberg FUNctions! fun generateRandomNumber(): Int { return 4 }

  109. @ToddGinsberg FUNctions! fun generateRandomNumber(): Int { return 4 } fun

    generateRandomNumber(): Int = 4
  110. @ToddGinsberg FUNctions! fun generateRandomNumber(): Int { return 4 } fun

    generateRandomNumber(): Int = 4 fun generateRandomNumber() = 4
  111. @ToddGinsberg FUNctions - Default Values fun random(offset: Int = 0):

    Int = offset + 4
  112. @ToddGinsberg FUNctions - Default Values fun random(offset: Int = 0):

    Int = offset + 4 random() // 4
  113. @ToddGinsberg FUNctions - Default Values fun random(offset: Int = 0):

    Int = offset + 4 random() // 4 random(1) // 5
  114. @ToddGinsberg FUNctions - Adding Parameters fun combine(first: Int, second: Int):

    Int = first + second
  115. @ToddGinsberg FUNctions - Adding Parameters fun combine(first: Int, second: Int,

    third: Int = 0): Int = first + second + third
  116. @ToddGinsberg FUNctions - Adding Parameters fun combine(first: Int, second: Int,

    third: Int = 0): Int = first + second + third combine(1, 2) combine(1, 2, 3)
  117. @ToddGinsberg FUNctions - Named Parameters fun combine(first: Int, second: Int):

    Int = first + second
  118. @ToddGinsberg FUNctions - Named Parameters fun combine(first: Int, second: Int):

    Int = first + second combine(1, 2) // 3
  119. @ToddGinsberg FUNctions - Named Parameters fun combine(first: Int, second: Int):

    Int = first + second combine(first = 1, second = 2) // 3
  120. @ToddGinsberg FUNctions - Named Parameters fun combine(first: Int, second: Int):

    Int = first + second combine(second = 2, first = 1) // 3
  121. @ToddGinsberg FUNction Names @Test fun replicantsHaveAFourYearLifespan() { // ... }

  122. @ToddGinsberg FUNction Names @Test fun `Replicants have a four year

    lifespan!`() { // ... }
  123. @ToddGinsberg Operator Overloading Limited to predefined operators! Expression Translated to

    a + b a.plus(b) a - b a.minus(b) a * b a.times(b) a / b a.div(b) a % b a.rem(b) a..b a.rangeTo(b)
  124. @ToddGinsberg Operator Overloading Limited to predefined operators! Expression Translated to

    a in b b.contains(a) a !in b !b.contains(a)
  125. @ToddGinsberg Operator Overloading Limited to predefined operators! Expression Translated to

    a[i] a.get(i) a[i] = b a.set(i, b)
  126. @ToddGinsberg Operator Overloading Limited to predefined operators! Expression Translated to

    a += b a.plusAssign(b) a -= b a.minusAssign(b) a *= b a.timesAssign(b) a /= b a.divAssign(b) a %= b a.remAssign(b)
  127. @ToddGinsberg Operator Overloading Limited to predefined operators! Expression Translated to

    a > b a.compareTo(b) > 0 a < b a.compareTo(b) < 0 a >= b a.compareTo(b) >= 0 a <= b a.compareTo(b) <= 0
  128. @ToddGinsberg Extension FUNctions // Java public static boolean isEven(int i)

    { return i % 2 == 0; }
  129. @ToddGinsberg Extension FUNctions // Kotlin fun Int.isEven(): Boolean = this

    % 2 == 0
  130. @ToddGinsberg Extension FUNctions // Kotlin fun Int.isEven(): Boolean = this

    % 2 == 0 2.isEven() // True!
  131. @ToddGinsberg The use Extension // Java try (Connection conn =

    getConnection()) { // ... }
  132. @ToddGinsberg The use Extension // Kotlin getConnection().use { conn ->

    // ... }
  133. @ToddGinsberg The apply Extension // Expression and Statements val p

    = Person() p.name = "Dr. Robert Hume" p.age = 47
  134. @ToddGinsberg The apply Extension // Single Expression val p =

    Person().apply { name = "Dr. Robert Hume" age = 47 }
  135. @ToddGinsberg The apply Extension // Single Expression val p =

    Person().apply { name = "Dr. Robert Hume" age = 47 address = Address().apply { line1 = "5 Tall Cedar Rd." … } }
  136. @ToddGinsberg Higher-Order Functions fun measureTimeMillis(block: () -> Unit): Long {

    val start = System.currentTimeMillis() block() return System.currentTimeMillis() - start }
  137. @ToddGinsberg Higher-Order Functions fun measureTimeMillis(block: () -> Unit): Long {

    val start = System.currentTimeMillis() block() return System.currentTimeMillis() - start } val time = measureTimeMillis { someSlowQuery() }
  138. @ToddGinsberg One More Thing on FUNctions • Functions are final

    by default.
  139. @ToddGinsberg One More Thing on FUNctions • Functions are final

    by default. • Arguments are always final.
  140. @ToddGinsberg One More Thing on FUNctions • Functions are final

    by default. • Arguments are always final. • Functions can be defined in a file, outside of a class.
  141. @ToddGinsberg One More Thing on FUNctions • Functions are final

    by default. • Arguments are always final. • Functions can be defined in a file, outside of a class. • Functions can be defined within another function.
  142. @ToddGinsberg One More Thing on FUNctions • Functions are final

    by default. • Arguments are always final. • Functions can be defined in a file, outside of a class. • Functions can be defined within another function. • Kotlin supports tail recursive functions.
  143. @ToddGinsberg What About Checked Exceptions? ?

  144. @ToddGinsberg What About Checked Exceptions? NO

  145. @ToddGinsberg Lambdas listOf(1, 2, 3, 4) .filter { x ->

    x % 2 == 0 } .map { y -> y * 2 } // List[4, 8]
  146. @ToddGinsberg Lambdas listOf(1, 2, 3, 4) .filter { x ->

    x % 2 == 0 } .map { y -> y * 2 } // List[4, 8]
  147. @ToddGinsberg Lambdas listOf(1, 2, 3, 4) .filter { it %

    2 == 0 } .map { it * 2 } // List[4, 8]
  148. @ToddGinsberg Type Aliases fun doSomethingWithMap( ops: Map<SomeType, List<String>> ) {

    ops.entries // ... }
  149. @ToddGinsberg Type Aliases fun doSomethingWithMap( ops: Map<SomeType, List<String>> ) {

    ops.entries // ... }
  150. @ToddGinsberg Type Aliases typealias Operations = Map<SomeType, List<String>> fun doSomethingWithMap(ops:

    Operations) { ops.entries // … }
  151. @ToddGinsberg Import Aliases import java.util.Date import java.sql.Date

  152. @ToddGinsberg Import Aliases import java.util.Date as UtilDate import java.sql.Date as

    SqlDate
  153. @ToddGinsberg Import Aliases import java.util.Date as SqlDate import java.sql.Date as

    UtilDate
  154. @ToddGinsberg Import Aliases import java.util.Date as SqlDate import java.sql.Date as

    UtilDate
  155. @ToddGinsberg Reified Generics // Ugly val log = Logger.getLogger(Metrics::class.java) //

    What I want val log = loggerOf<Metrics>()
  156. @ToddGinsberg Reified Generics // Thanks type erasure :( fun <T>

    loggerOf(): Logger = Logger.getLogger(T::class.java)
  157. @ToddGinsberg Reified Generics inline fun <reified T> loggerOf(): Logger =

    Logger.getLogger(T::class.java)
  158. @ToddGinsberg Reified Generics inline fun <reified T> loggerOf(): Logger =

    Logger.getLogger(T::class.java) // Works! val log = loggerOf<Metrics>()
  159. @ToddGinsberg So Many Things Left Out! :( - Companion objects

    - Massive STDLIB replaces your util classes - Inline classes - Unsigned integers - Multiplatform - Coroutines - DSL support - Contracts - Infix functions
  160. @ToddGinsberg Summary

  161. @ToddGinsberg Make “Bad” Choices Explicit or Impossible Fixed in Kotlin:

    - Singleton support built in - Override keyword mandatory - Properties over fields with getter/setter - Mutability is minimized (val + collections) - Inheritance prohibited by default - Builders are easy with default values - No checked exceptions - Structural equality is the same everywhere - Delegation support makes composition easier
  162. @ToddGinsberg Why Do I Use Kotlin? • I write far

    less code
  163. @ToddGinsberg Why Do I Use Kotlin? • I write far

    less code • The code I write is more expressive and clear
  164. @ToddGinsberg Why Do I Use Kotlin? • I write far

    less code • The code I write is more expressive and clear • I avoid whole classes of defects
  165. @ToddGinsberg Why Do I Use Kotlin? • I write far

    less code • The code I write is more expressive and clear • I avoid whole classes of defects • Allows me to write in a more functional style
  166. @ToddGinsberg Why Do I Use Kotlin? • I write far

    less code • The code I write is more expressive and clear • I avoid whole classes of defects • Allows me to write in a more functional style • Plays well with the tools I use (Spring, IDEA, Gradle)
  167. @ToddGinsberg Why Do I Use Kotlin? • I write far

    less code • The code I write is more expressive and clear • I avoid whole classes of defects • Allows me to write in a more functional style • Plays well with the tools I use (Spring, IDEA, Gradle) • Writing Kotlin, for me, is more fun
  168. @ToddGinsberg Quantified Fun! % of developers who are developing with

    the language or technology but have not expressed interest in continuing to do so (Stack Overflow Developer Survey 2019)
  169. @ToddGinsberg Quantified Fun! % of developers who are developing with

    the language or technology but have not expressed interest in continuing to do so (Stack Overflow Developer Survey 2019) Kotlin
  170. @ToddGinsberg Want to Learn More? https://play.kotlinlang.org

  171. @ToddGinsberg Want to Learn More? https://www.coursera.org/learn/kotlin-for-java-developers

  172. #springone @s1p @ToddGinsberg todd@ginsberg.com https://todd.ginsberg.com Thank You!