10 Kotlin Tricks in 10(ish) Minutes (Devoxx US 2017)

10 Kotlin Tricks in 10(ish) Minutes (Devoxx US 2017)

Kotlin is new language growing in popularity as a complement to Java. Its major advantages and features compared to Java are immediately appealing. While it's quick to learn, it also has a lot of small and thoughtful parts which can be harder to discover. This short talk will cover 10 of my favorites with real-world examples. Attendees should come in having seen some Kotlin but looking to learn even more.

Video: https://youtu.be/0sPzDwS55wM

E68309f117985270285ade8082f4877d?s=128

Jake Wharton

March 21, 2017
Tweet

Transcript

  1. Jake Wharton 10 Kotlin Tricks In 10(ish) Minutes

  2. Why Kotlin?

  3. Why Kotlin? val firstName: String = "Jake"
 val lastName: String?

    = null
  4. Why Kotlin? val firstName = "Jake"
 val lastName: String? =

    null
  5. Why Kotlin? class User {
 public String getName() {
 //

    ...
 }
 public void setName(String name) {
 // ...
 }
 } // ^^^ Java
  6. Why Kotlin? class User {
 public String getName() {
 //

    ...
 }X
 public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is " + user.name)
  7. Why Kotlin? class User {
 public String getName() {
 //

    ...
 }X
 public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is ${user.name}")
  8. Why Kotlin? class User {
 public String getName() {
 //

    ...
 }X
 public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is $user")
  9. Why Kotlin? val user = User()

  10. Why Kotlin? val user = User()
 user = User()
 


    var currentUser = User()
 currentUser = User()
  11. Why Kotlin? fun Date.isTuesday(): Boolean {
 return day == 2


    }
  12. Why Kotlin? fun Date.isTuesday(): Boolean {
 return day == 2


    } val epoch = Date(1970, 0, 0)
 if (epoch.isTuesday()) {
 println("The epoch was a tuesday.")
 } else {
 println("The epoch was not a tuesday.")
 }
  13. Why Kotlin? val executor = Executors.newSingleThreadExecutor();
 executor.execute { println("Background thread!")

    }
  14. Why Kotlin? fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {


    // ...
 }
  15. Why Kotlin? fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {


    // ...
 }X
  16. Why Kotlin? inline fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>

    {
 // ...
 }X
  17. Why Kotlin? inline fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>

    {
 // ...
 }
  18. Why Kotlin? data class User(val firstName: String, val lastName: String)

  19. Why Kotlin? ...and much, much more!

  20. #01 Explosive Placeholders

  21. #01 Explosive Placeholders class Test : Runnable {
 override fun

    run() {
 // TODO doSomethingElse()?
 }B
 }A
  22. #01 Explosive Placeholders class Test : Runnable {Y
 override fun

    run() {Z
 if (something()) {
 doSomething()
 } else {
 // TODO doSomethingElse()?
 }C
 }B
 }A
  23. #01 Explosive Placeholders class Test : Runnable {Y
 override fun

    run() {Z
 if (something()) {
 doSomething()
 } else {
 // TODO doSomethingElse()?
 }C
 }B
 }A
  24. #01 Explosive Placeholders class Test : Callable<String> {Y
 override fun

    call(): String {Z
 if (something()) {
 return doSomething()
 } else {
 // TODO return doSomethingElse()?
 }C
 }B
 }A Runnable
 run
  25. #01 Explosive Placeholders class Test : Callable<String> {Y
 override fun

    call(): String {Z
 if (something()) {
 return doSomething()
 } else {
 // TODO return doSomethingElse()?
 }C
 }B
 }A Runnable
 run
  26. #01 Explosive Placeholders class Test : Callable<String> {
 override fun

    call(): String {
 if (something()) {
 return doSomething()
 } else {
 throw UnsupportedOperationException("TODO doSomethingElse()?")
 }C
 }B
 }A 
 
 
 
 
 // return
  27. #01 Explosive Placeholders class Test : Callable<String> {
 override fun

    call(): String {
 if (something()) {
 return doSomething()
 } else {
 TODO("doSomethingElse()?")
 }C
 }B
 }A
  28. #01 Explosive Placeholders class Test : Callable<String> {
 override fun

    call(): String {
 if (something()) {
 return doSomething()
 } else {
 TODO("doSomethingElse()?")
 }C
 }B
 }A
  29. #02 Semantic Validation

  30. #02 Semantic Validation static String join(String sep, List<String> strings) {


    // ...
 }Z
  31. #02 Semantic Validation static String join(String sep, List<String> strings) {


    if (sep == null) throw new NullPointerException("sep == null");
 if (strings == null) throw new NullPointerException("strings == null"); 
 // ...
 }Z
  32. #02 Semantic Validation static String join(String sep, List<String> strings) {


    if (sep == null) throw new NullPointerException("sep == null");
 if (sep.length() < 2) { throw new IllegalArgumentException("sep.length() < 2: " + sep); }Y
 if (strings == null) throw new NullPointerException("strings == null"); 
 // ...
 }Z
  33. #02 Semantic Validation static String join(String sep, List<String> strings) {


    Preconditions.checkNotNull(sep, "sep == null");
 if (sep.length() < 2) {
 throw new IllegalArgumentException("sep.length() < 2: " + sep);
 }Y
 Preconditions.checkNotNull(strings, "strings == null");
 
 // ...
 }Z 
 if == null) throw new NullPointerException(
 
 if == null) throw new NullPointerException(
  34. #02 Semantic Validation static String join(String sep, List<String> strings) {


    Preconditions.checkNotNull(sep, "sep == null");
 Preconditions.checkArgument(sep.length() < 2, "sep.length() < 2: " + sep);
 Preconditions.checkNotNull(strings, "strings == null");
 
 // ...
 }Z 
 
 if ) {
 throw new IllegalArgumentException(
 }Y
  35. #02 Semantic Validation static String join(String sep, List<String> strings) {


    Preconditions.checkNotNull(sep, "sep == null");
 Preconditions.checkArgument(sep.length() < 2, "sep.length() < 2: " + sep);
 Preconditions.checkNotNull(strings, "strings == null");
 
 // ...
 }Z 
 
 if ) {
 throw new IllegalArgumentException(
 }Y
  36. #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    Preconditions.checkArgument(sep.length < 2, "sep.length < 2: " + sep)
 // ...
 }Z static 
 Preconditions.checkNotNull(sep, "sep == null");
 ;
 Preconditions.checkNotNull(strings, "strings == null");
  37. #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    require(sep.length < 2) { "sep.length < 2: " + sep }
 // ...
 }Z 
 Preconditions.checkArgument , )
  38. #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    require(sep.length < 2) { "sep.length < 2: " + sep }
 // ...
 }Z
  39. #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    require(sep.length < 2) { "sep.length < 2: " + sep }
 // ...
 }Z 
 Preconditions.checkArgument , )
  40. #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    if (sep.length < 2) { throw IllegalArgumentException("sep.length < 2: " + sep) }
 // ...
 }Z 
 require ) { }
  41. #02 Semantic Validation // throws IllegalArgumentException require(value: Boolean) require(value: Boolean,

    lazyMessage: () -> Any) requireNotNull(value: T?): T requireNotNull(value: T?, lazyMessage: () -> Any): T // throws IllegalStateException check(value: Boolean) check(value: Boolean, lazyMessage: () -> Any) checkNotNull(value: T?): T checkNotNull(value: T?, lazyMessage: () -> Any): T // throws AssertionError assert(value: Boolean) assert(value: Boolean, lazyMessage: () -> Any)
  42. #03 Anything and Nothing

  43. #03 Anything and Nothing Runnable

  44. #03 Anything and Nothing Runnable Any

  45. #03 Anything and Nothing Runnable Int Any

  46. #03 Anything and Nothing Runnable Int Any Nothing

  47. #03 Anything and Nothing fun printName(user: User?) {
 val name

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }
  48. #03 Anything and Nothing fun printName(user: User?) {
 val name

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  49. #03 Anything and Nothing fun printName(user: User?) {
 val name

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  50. #03 Anything and Nothing fun printName(user: User?) {
 val name

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  51. #03 Anything and Nothing fun printName(user: User?) {
 val name

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  52. #03 Anything and Nothing fun printName(user: User?) {
 if (user

    != null) {
 println("Name is ${user.name}.")
 }Y
 throw IllegalArgumentException("User was null")
 }Z 
 val name = ?.name ?:
  53. #03 Anything and Nothing fun printName(user: User?) {
 if (user

    != null) {
 println("Name is ${user.name}.")
 }Y
 throw IllegalArgumentException("User was null")
 }Z
  54. #03 Anything and Nothing fun printName(user: User?) {
 if (user

    != null) {
 println("Name is ${user.name}.")
 }Y
 throw IllegalArgumentException("User was null")
 println("A user has no name.")
 }Z
  55. #03 Anything and Nothing fun runServer(serverSocket: ServerSocket) {
 while (true)

    {
 handleSocket(serverSocket.accept())
 }
 }
  56. #03 Anything and Nothing fun runServer(serverSocket: ServerSocket) {X
 while (true)

    {
 handleSocket(serverSocket.accept())
 }Y
 }Z runServer(ServerSocket(80))
 println("Running!")
  57. #03 Anything and Nothing fun runServer(serverSocket: ServerSocket): Nothing {X
 while

    (true) {
 handleSocket(serverSocket.accept())
 }Y
 }Z runServer(ServerSocket(80))
 println("Running!")
  58. #03 Anything and Nothing fun runServer(serverSocket: ServerSocket): Nothing {
 while

    (true) {
 handleSocket(serverSocket.accept())
 }
 } runServer(ServerSocket(80))
 println("Running!")
  59. #04 Let

  60. #04 Let val user: User? = null
 
 if (user

    != null) {X
 // user not null
 }X
  61. #04 Let var user: User? = null
 
 if (user

    != null) {X
 // user might be null
 }X
  62. #04 Let var user: User? = null
 
 user?.let {X


    // it == user not null
 }X 
 
 if ( != null)
  63. #04 Let var user: User? = null
 
 user?.let {

    
 // it == user not null, only read once!
 }X
  64. #04 Let class Foo {
 @Volatile var user: User? =

    null
 
 fun doSomething() {
 user?.let { user ->
 // user not null, only read once!
 }X
 }Y
 }Z 
 
 
 it ==
  65. #04 Let class Foo {
 @Volatile var user: User? =

    null
 
 fun doSomething() {
 user?.apply { user ->
 // user not null, only read once!
 }X
 }Y
 }Z 
 
 
 
 let
  66. #04 Let class Foo {
 @Volatile var user: User? =

    null
 
 fun doSomething() {
 user?.run { user ->
 // user not null, only read once!
 }X
 }Y
 }Z
  67. #04 Let class Foo {
 @Volatile var user: User? =

    null
 
 fun doSomething() {
 user?.also { user ->
 // user not null, only read once!
 }X
 }Y
 }Z
  68. #04 Let someMethod().let { result ->
 // use result multiple

    times
 }X
  69. #05 Multiline String Literals

  70. #05 Multiline String Literals val string = "foo"Z

  71. #05 Multiline String Literals val string = "foo\nbar\nbaz"Z

  72. #05 Multiline String Literals val string = "foo\n" + "bar\n"

    + "baz"Z
  73. #05 Multiline String Literals val string = """foo bar baz"""Z

  74. #05 Multiline String Literals val string = """
 foo
 bar


    baz""".trimIndent()
  75. #05 Multiline String Literals val string = """
 foo
 bar


    baz""".trimIndent() 
 
 
 Margin
  76. #05 Multiline String Literals val string = """|foo
 1|bar
 2|baz""".trimMargin()

    
 
 Indent
  77. #05 Multiline String Literals val foo = "FOO!"
 val bar

    = "BAR!"
 val baz = "BAZ!"
 
 val string = """|$foo
 1|$bar
 2|$baz""".trimMargin()
  78. #06 Lazy but Speedy

  79. #06 Lazy but Speedy class NamePrinter(val firstName: String, val lastName:

    String) {
 }Z
  80. #06 Lazy but Speedy class NamePrinter(val firstName: String, val lastName:

    String) {
 fun printName() {
 println("$firstName $lastName")
 }Y
 }Z
  81. #06 Lazy but Speedy class NamePrinter(val firstName: String, val lastName:

    String) {
 var fullName: String? = null
 
 fun printName() {
 if (fullName == null) {
 fullName = "$firstName $lastName"
 }X
 println(fullName)
 }Y
 }Z
  82. #06 Lazy but Speedy class NamePrinter(val firstName: String, val lastName:

    String) {
 val fullName: String by lazy { "$firstName $lastName" }X
 
 fun printName() {
 println(fullName)
 }Y
 }Z 
 r ? = null
 
 
 if (fullName == null) {
 fullName =
 }X
  83. #06 Lazy but Speedy class NamePrinter(val firstName: String, val lastName:

    String) {
 val fullName: String by lazy { "$firstName $lastName" }X
 
 fun printName() {
 println(fullName)
 }Y
 }Z
  84. #06 Lazy but Speedy import kotlin.LazyThreadSafetyMode.NONE
 
 class NamePrinter(val firstName:

    String, val lastName: String) {
 val fullName: String by lazy(NONE) { "$firstName $lastName" }X
 
 fun printName() {
 println(fullName)
 }Y
 }Z
  85. #07 Code Block Measurement

  86. #07 Code Block Measurement val helloStart = System.currentTimeMillis()
 println("Hello, world!")


    val helloTook = System.currentTimeMillis() - helloStart
 println("Saying 'hello' took ${helloTook}ms")
  87. #07 Code Block Measurement val helloTook = measureTimeMillis {
 println("Hello,

    world!")
 }X
 
 println("Saying 'hello' took ${helloTook}ms") val helloStart = System.currentTimeMillis()
 
 System.currentTimeMillis() - helloStart
  88. #07 Code Block Measurement val helloTook = measureTimeNanos {
 println("Hello,

    world!")
 }X
 
 println("Saying 'hello'Ztook ${helloTook}ns")
  89. #07 Code Block Measurement var helloTook = measureTimeNanos {
 println("Hello,

    world!")
 }X helloTook += measureTimeNanos {
 println("Hello, world!")
 }X
 
 println("Saying 'hello' twiceZtook ${helloTook}ns") l
  90. #08 Deprecation Levels

  91. #08 Deprecation Levels fun join(sep: String, strings: List<String>): String {

    
 // ...
 }Z
  92. #08 Deprecation Levels @Deprecated
 fun join(sep: String, strings: List<String>): String

    { 
 // ...
 }Z
  93. #08 Deprecation Levels @Deprecated
 fun join(sep: String, strings: List<String>): String

    { 
 // ...
 }Z
  94. #08 Deprecation Levels @Deprecated("Use strings.joinToString(sep).")
 fun join(sep: String, strings: List<String>):

    String { 
 // ...
 }Z
  95. #08 Deprecation Levels @Deprecated("Use strings.joinToString(sep).")X
 fun join(sep: String, strings: List<String>):

    String { 
 // ...
 }Z join(", ", listOf("me", "you"))
  96. #08 Deprecation Levels import kotlin.DeprecationLevel.ERROR
 
 @Deprecated("Use strings.joinToString(sep).", level =

    ERROR)X
 fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))
  97. #08 Deprecation Levels import kotlin.DeprecationLevel.ERROR
 
 @Deprecated("Use strings.joinToString(sep).", level =

    ERROR)X
 fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))
  98. #08 Deprecation Levels import kotlin.DeprecationLevel.ERROR
 
 @Deprecated("Use strings.joinToString(sep).", level =

    ERROR)X
 fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you")) Error:(10, 1) Kotlin: Using 'join(String, List<String>): String' is an error. Use strings.joinToString(sep).
  99. #08 Deprecation Levels import kotlin.DeprecationLevel.HIDDEN
 
 @Deprecated("Use strings.joinToString(sep).", level =

    HIDDEN)X
 fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you")) ERROR ERROR Error:(10, 1) Kotlin: Using 'join(String, List<String>): String' is an error. Use strings.joinToString(sep).
  100. #08 Deprecation Levels import kotlin.DeprecationLevel.HIDDEN
 
 @Deprecated("Use strings.joinToString(sep).", level =

    HIDDEN)
 fun join(sep: String, strings: List<String>): String { 
 // ...
 }
 
 join(", ", listOf("me", "you"))
  101. #09 Deprecation Replacements

  102. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).")X
 fun join(sep: String, strings: List<String>):

    String { 
 // ...
 }Z join(", ", listOf("me", "you"))
  103. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).",
 replaceWith = ReplaceWith("strings.joinToString(sep)"))X
 fun join(sep:

    String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))
  104. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).",
 replaceWith = ReplaceWith("strings.joinToString(sep)"))X
 fun join(sep:

    String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))
  105. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).",
 replaceWith = ReplaceWith("strings.joinToString(sep)"))X
 fun join(sep:

    String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))
  106. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).",
 replaceWith = ReplaceWith("strings.joinToString(sep)"))X
 fun join(sep:

    String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))
  107. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).",
 replaceWith = ReplaceWith("strings.joinToString(sep)"))X
 fun join(sep:

    String, strings: List<String>): String { 
 // ...
 }Z
 
 listOf("me", "you").joinToString(", ")
  108. #09 Deprecation Replacements @Deprecated("Use strings.joinToString(sep).",
 replaceWith = ReplaceWith("strings.joinToString(sep)"))X
 fun join(sep:

    String, strings: List<String>): String { 
 // ...
 }Z 
 
 
 
 
 
 join(", ", listOf("me", "you"))
  109. #09 Deprecation Replacements @Deprecated("Use Guava's Joiner.",
 replaceWith = ReplaceWith("Joiner.on(sep).join(strings)",
 imports

    = "com.google.common.base.Joiner"))X fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you")) strings.joinToString(sep) 
 .joinToString(
  110. #09 Deprecation Replacements @Deprecated("Use Guava's Joiner.",
 replaceWith = ReplaceWith("Joiner.on(sep).join(strings)",
 imports

    = "com.google.common.base.Joiner"))X fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 join(", ", listOf("me", "you"))X 
 
 
 
 Joiner.on(", ").join(listOf("me", "you"))
  111. #09 Deprecation Replacements import com.google.common.base.Joiner
 @Deprecated("Use Guava's Joiner.",
 replaceWith =

    ReplaceWith("Joiner.on(sep).join(strings)",
 imports = "com.google.common.base.Joiner"))X fun join(sep: String, strings: List<String>): String { 
 // ...
 }Z
 
 Joiner.on(", ").join(listOf("me", "you")) 
 
 
 
 
 
 join(", ", listOf("me", "you"))X
  112. #10 Erasing Erasure

  113. #10 Erasing Erasure class Foo : Callable<String>, Callable<Int> {
 }

  114. #10 Erasing Erasure class Foo : Callable<String>, Callable<Int> {
 }

  115. #10 Erasing Erasure fun sort(strings: List<String>) {
 // ...
 }Y

    
 fun sort(ints: List<Int>) {
 // ...
 }Z
  116. #10 Erasing Erasure fun sort(strings: List) {
 // ...
 }Y

    
 fun sort(ints: List) {
 // ...
 }Z
  117. #10 Erasing Erasure fun sort(strings: List<String>) {
 // ...
 }Y

    
 fun sort(ints: List<Int>) {
 // ...
 }Z
  118. #10 Erasing Erasure fun sortStrings(strings: List<String>) {
 // ...
 }Y

    
 fun sortInts(ints: List<Int>) {
 // ...
 }Z
  119. #10 Erasing Erasure fun sort(strings: List<String>) {
 // ...
 }Y

    
 fun sort(ints: List<Int>) {
 // ...
 }Z
  120. #10 Erasing Erasure @JvmName("sortStrings")
 fun sort(strings: List<String>) {
 // ...


    }Y
 @JvmName("sortInts")
 fun sort(ints: List<Int>) {
 // ...
 }Z
  121. kotlinlang.org

  122. jakewharton jakewharton jakewharton twitter.com/ github.com/ .com 10 Kotlin Tricks In

    10(ish) Minutes