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. 5.

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

    ...
 }
 public void setName(String name) {
 // ...
 }
 } // ^^^ Java
  2. 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)
  3. 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}")
  4. 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")
  5. 10.

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


    var currentUser = User()
 currentUser = User()
  6. 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.")
 }
  7. 21.

    #01 Explosive Placeholders class Test : Runnable {
 override fun

    run() {
 // TODO doSomethingElse()?
 }B
 }A
  8. 22.

    #01 Explosive Placeholders class Test : Runnable {Y
 override fun

    run() {Z
 if (something()) {
 doSomething()
 } else {
 // TODO doSomethingElse()?
 }C
 }B
 }A
  9. 23.

    #01 Explosive Placeholders class Test : Runnable {Y
 override fun

    run() {Z
 if (something()) {
 doSomething()
 } else {
 // TODO doSomethingElse()?
 }C
 }B
 }A
  10. 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
  11. 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
  12. 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
  13. 27.

    #01 Explosive Placeholders class Test : Callable<String> {
 override fun

    call(): String {
 if (something()) {
 return doSomething()
 } else {
 TODO("doSomethingElse()?")
 }C
 }B
 }A
  14. 28.

    #01 Explosive Placeholders class Test : Callable<String> {
 override fun

    call(): String {
 if (something()) {
 return doSomething()
 } else {
 TODO("doSomethingElse()?")
 }C
 }B
 }A
  15. 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
  16. 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
  17. 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(
  18. 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
  19. 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
  20. 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");
  21. 37.

    #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    require(sep.length < 2) { "sep.length < 2: " + sep }
 // ...
 }Z 
 Preconditions.checkArgument , )
  22. 38.

    #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    require(sep.length < 2) { "sep.length < 2: " + sep }
 // ...
 }Z
  23. 39.

    #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    require(sep.length < 2) { "sep.length < 2: " + sep }
 // ...
 }Z 
 Preconditions.checkArgument , )
  24. 40.

    #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


    if (sep.length < 2) { throw IllegalArgumentException("sep.length < 2: " + sep) }
 // ...
 }Z 
 require ) { }
  25. 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)
  26. 47.

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

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }
  27. 48.

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

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  28. 49.

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

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  29. 50.

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

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  30. 51.

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

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  31. 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 ?:
  32. 53.

    #03 Anything and Nothing fun printName(user: User?) {
 if (user

    != null) {
 println("Name is ${user.name}.")
 }Y
 throw IllegalArgumentException("User was null")
 }Z
  33. 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
  34. 55.
  35. 56.

    #03 Anything and Nothing fun runServer(serverSocket: ServerSocket) {X
 while (true)

    {
 handleSocket(serverSocket.accept())
 }Y
 }Z runServer(ServerSocket(80))
 println("Running!")
  36. 57.

    #03 Anything and Nothing fun runServer(serverSocket: ServerSocket): Nothing {X
 while

    (true) {
 handleSocket(serverSocket.accept())
 }Y
 }Z runServer(ServerSocket(80))
 println("Running!")
  37. 58.

    #03 Anything and Nothing fun runServer(serverSocket: ServerSocket): Nothing {
 while

    (true) {
 handleSocket(serverSocket.accept())
 }
 } runServer(ServerSocket(80))
 println("Running!")
  38. 59.
  39. 60.

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

    != null) {X
 // user not null
 }X
  40. 61.

    #04 Let var user: User? = null
 
 if (user

    != null) {X
 // user might be null
 }X
  41. 62.

    #04 Let var user: User? = null
 
 user?.let {X


    // it == user not null
 }X 
 
 if ( != null)
  42. 63.

    #04 Let var user: User? = null
 
 user?.let {

    
 // it == user not null, only read once!
 }X
  43. 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 ==
  44. 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
  45. 66.

    #04 Let class Foo {
 @Volatile var user: User? =

    null
 
 fun doSomething() {
 user?.run { user ->
 // user not null, only read once!
 }X
 }Y
 }Z
  46. 67.

    #04 Let class Foo {
 @Volatile var user: User? =

    null
 
 fun doSomething() {
 user?.also { user ->
 // user not null, only read once!
 }X
 }Y
 }Z
  47. 75.
  48. 77.

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

    = "BAR!"
 val baz = "BAZ!"
 
 val string = """|$foo
 1|$bar
 2|$baz""".trimMargin()
  49. 80.

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

    String) {
 fun printName() {
 println("$firstName $lastName")
 }Y
 }Z
  50. 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
  51. 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
  52. 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
  53. 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
  54. 86.

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


    val helloTook = System.currentTimeMillis() - helloStart
 println("Saying 'hello' took ${helloTook}ms")
  55. 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
  56. 88.

    #07 Code Block Measurement val helloTook = measureTimeNanos {
 println("Hello,

    world!")
 }X
 
 println("Saying 'hello'Ztook ${helloTook}ns")
  57. 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
  58. 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"))
  59. 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"))
  60. 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).
  61. 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).
  62. 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"))
  63. 103.
  64. 104.
  65. 105.
  66. 106.
  67. 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(", ")
  68. 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"))
  69. 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(
  70. 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"))
  71. 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
  72. 115.

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

    
 fun sort(ints: List<Int>) {
 // ...
 }Z
  73. 116.

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

    
 fun sort(ints: List) {
 // ...
 }Z
  74. 117.

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

    
 fun sort(ints: List<Int>) {
 // ...
 }Z
  75. 118.

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

    
 fun sortInts(ints: List<Int>) {
 // ...
 }Z
  76. 119.

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

    
 fun sort(ints: List<Int>) {
 // ...
 }Z
  77. 120.

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


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