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

10 Kotlin Tricks in 10ish Minutes (Square NYC, ...

10 Kotlin Tricks in 10ish Minutes (Square NYC, November 2016)

Kotlin is a new and popular language for Android development. Its major advantages and features are immediately appealing and quick to learn, but it also has a lot of small and thoughtful parts which are harder to discover. This short talk will cover 10 of my favorites with real-world examples.

Video: https://www.youtube.com/watch?v=YKzUbeUtTak

Jake Wharton

November 02, 2016
Tweet

More Decks by Jake Wharton

Other Decks in Programming

Transcript

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

    ...
 }
 public void setName(String name) {
 // ...
 }
 } // ^^^ Java
  2. 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. 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. 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. Why Kotlin? val user = User()
 user = User()
 


    var currentUser = User()
 currentUser = User()
  6. 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. #01 Explosive Placeholders class Test : Runnable {
 override fun

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

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

    run() {Z
 if (something()) {
 doSomething()
 } else {
 // TODO doSomethingElse()?
 }C
 }B
 }A
  10. #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. #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. #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. #01 Explosive Placeholders class Test : Callable<String> {
 override fun

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

    call(): String {
 if (something()) {
 return doSomething()
 } else {
 TODO("doSomethingElse()?")
 }C
 }B
 }A
  15. #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. #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. #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. #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. #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. #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. #02 Semantic Validation fun join(sep: String, strings: List<String>): String {


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


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


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


    if (sep.length < 2) { throw IllegalArgumentException("sep.length < 2: " + sep) }
 // ...
 }Z 
 require ) { }
  25. #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. #03 Anything and Nothing fun printName(user: User?) {
 val name

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

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

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

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

    = user?.name ?: throw IllegalArgumentException("User was null")
 println("Name is $name.")
 }Z
  31. #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. #03 Anything and Nothing fun printName(user: User?) {
 if (user

    != null) {
 println("Name is ${user.name}.")
 }Y
 throw IllegalArgumentException("User was null")
 }Z
  33. #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. #03 Anything and Nothing fun runServer(serverSocket: ServerSocket) {X
 while (true)

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

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

    (true) {
 handleSocket(serverSocket.accept())
 }
 } runServer(ServerSocket(80))
 println("Running!")
  37. #04 Let val user: User? = null
 
 if (user

    != null) {X
 // user not null
 }X
  38. #04 Let val user: User? = null
 
 user?.let {X


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

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

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

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

    null
 
 fun doSomething() {
 user?.run {
 // user not null, only read once!
 }X
 }Y
 }Z
  43. #04 Let val user: User? = null
 
 someMethod().let {

    result ->
 // use result multiple times
 }X
  44. #05 Multiline String Literals val foo = "FOO!"
 val bar

    = "BAR!"
 val baz = "BAZ!"
 
 val string = """|$foo
 1|$bar
 2|$baz""".trimMargin()
  45. #06 Lazy but Speedy class NamePrinter(val firstName: String, val lastName:

    String) {
 fun printName() {
 println("$firstName $lastName")
 }Y
 }Z
  46. #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
  47. #06 Lazy but Speedy class NamePrinter(val fullName: Lazy<String>) {
 fun

    printName() {
 println(fullName.get())
 }Y
 }Z @Module
 class NameModule(val firstName: String, val lastName: String) {
 @Provides fun provideFullName() = "$firstName $lastName"
 } 
 var ? = null
 
 
 if (fullName == null) {
 fullName
 

  48. #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
  49. #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
  50. #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
  51. #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
  52. #07 Code Block Measurement val helloStart = System.currentTimeMillis()
 println("Hello, world!")


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

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

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

    world!")
 }X helloTook += measureTimeNanos {
 println("Hello, world!")
 }X
 
 println("Saying 'hello' twiceZtook ${helloTook}ns") l
  56. #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"))
  57. #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"))
  58. #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).
  59. #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).
  60. #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"))
  61. #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(", ")
  62. #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"))
  63. #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(
  64. #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"))
  65. #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
  66. #10 Erasing Erasure fun sort(strings: List<String>) {
 // ...
 }Y

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

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

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

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

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


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