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

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

54879f243e5b72eedb2d379bed6fda27?s=128

Jake Wharton
PRO

March 21, 2017
Tweet

More Decks by Jake Wharton

Other Decks in Programming

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