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

Kotlin: Uncovered

Kotlin: Uncovered

Kotlin does a lot for us in the way of reducing boilerplate. But what is it really doing? We will be inspecting some decompiled Kotlin to discover how it does its job. By looking underneath at how it handles data classes, lambdas, and delegation, we can better understand how the language executes what we write. If you’re curious about the language, or already using it in production, you should walk away from this investigation with a deeper understanding of Kotlin, and some tools for continued exploration.

- As given at Droidcon Boston '17 and Chicago Roboto '17

Victoria Gonda

April 05, 2017
Tweet

More Decks by Victoria Gonda

Other Decks in Programming

Transcript

  1. @TTGonda collectiveidea.com DEFINE('BUBBLE(A,ALEN)I,J,UB,TMP') : (BUBBLE_END) BUBBLE I = 1; UB

    = ALEN OUTER GT(UB,1) :F(BDONE) J = 1 INNER LE(A<J>, A<J + 1>) :S(INCRJ) TMP = A<J> A<J> = A<J + 1> A<J + 1> = TMP INCRJ J = LT(J + 1,UB) J + 1 :S(INNER) UB = UB - 1 :(OUTER) BDONE BUBBLE = A :(RETURN) BUBBLE_END NUMBERS = '33 99 15 54 1 20 88 47 68 72' OUTPUT = "Unsorted List" OUTPUT = NUMBERS; NUMARRAY = ARRAY(10) FLOOP I = I + 1 NUMBERS SPAN('0123456789') . NUMARRAY<I> = :S(FLOOP) BUBBLE(NUMARRAY,10); NUMBERS = '' SLOOP J = J + 1; NUMBERS = NUMBERS NUMARRAY<J> ' ' :S(SLOOP) OUTPUT = TRIM(NUMBERS) END SNOBOL
  2. @TTGonda collectiveidea.com HAI 1.2 CAN HAS STDIO? VISIBLE "IS KITTAH

    GAME!" VISIBLE "ENTER 'HELP' FOR CONTROLS" VISIBLE "" I HAS A NAME VISIBLE "WHAT IS KITTAH'S NAME?" GIMMEH NAME I HAS A ACTION VISIBLE SMOOSH "WAT U WANTING TO DO WITH " NAME "?" MKAY GIMMEH ACTION IM IN YR LOOP NERFIN YR PURRS TIL BOTH SAEM ACTION AN "KBYE" BOTH SAEM ACTION AN "HELP", O RLY? YA RLY VISIBLE "PET" VISIBLE "FEED" VISIBLE "IS HAPPY?" VISIBLE "KBYE" MEBBE BOTH SAEM ACTION AN "PURRS" VISIBLE SMOOSH "PURRS: " PURRS MKAY MEBBE BOTH SAEM ACTION AN "FEED" VISIBLE SMOOSH "YOU DID FEED " NAME ". " NAME " IS HAPPY WITH FOOD." MKAY PURRS R SUM OF PURRS AN 3 LOLCODE
  3. @TTGonda collectiveidea.com public final class User { @NotNull private final

    String firstName; @Nullable private String lastName; public User(@NotNull String firstName, @Nullable String lastName) { Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } @NotNull public final String getFirstName() { return this.firstName; } @Nullable public final String getLastName() { return this.lastName; } public final void setLastName(@Nullable String var1) { this.lastName = var1; } }
  4. @TTGonda collectiveidea.com public User(@NotNull String firstName, @Nullable String lastName) {

    Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } public final class User { @NotNull private final String firstName; @Nullable private final String lastName;
  5. @TTGonda collectiveidea.com public User(@NotNull String firstName, @Nullable String lastName) {

    Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } public final class User { @NotNull private final String firstName; @Nullable private final String lastName; public static void checkParameterIsNotNull( Object value, String paramName) { if (value == null) { // prints error with stack trace throwParameterIsNullException(paramName); } }
  6. @TTGonda collectiveidea.com public User(@NotNull String firstName, @Nullable String lastName) {

    Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } public final class User { @NotNull private final String firstName; @Nullable private final String lastName; public static void checkParameterIsNotNull( Object value, String paramName) { if (value == null) { // prints error with stack trace throwParameterIsNullException(paramName); } } Caused by: java.lang.IllegalStateException: firstName must not be null at com.project.User.<init>(User.kt:8)
  7. @TTGonda collectiveidea.com public User(@NotNull String firstName, @Nullable String lastName) {

    Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } public final class User { @NotNull private final String firstName; @Nullable private final String lastName;
  8. @TTGonda collectiveidea.com @NotNull public final String getFirstName() { return this.firstName;

    } @Nullable public final String getLastName() { return this.lastName; } public final void setLastName(@Nullable String var1) { this.lastName = var1; } } Intrinsics.checkParameterIsNotNull(lastName, "lastName"); super(); this.firstName = firstName; this.lastName = lastName; }
  9. @TTGonda collectiveidea.com public final class User { @NotNull private final

    String firstName; @Nullable private String lastName; public User(@NotNull String firstName, @Nullable String lastName) { Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } @NotNull public final String getFirstName() { return this.firstName; } @Nullable public final String getLastName() { return this.lastName; } public final void setLastName(@Nullable String var1) { this.lastName = var1; } @NotNull public final String component1() { return this.firstName; } @Nullable public final String component2() { return this.lastName; } @NotNull public final User copy(@NotNull String firstName, @Nullable String lastName) { Intrinsics.checkParameterIsNotNull(firstName, "firstName"); return new User(firstName, lastName); } // $FF: synthetic method // $FF: bridge method @NotNull public static User copy$default(User var0, String var1, String var2, int var3, Object var4) { if((var3 & 1) != 0) { var1 = var0.firstName; } if((var3 & 2) != 0) { var2 = var0.lastName; } return var0.copy(var1, var2); } public String toString() { return "User(firstName=" + this.firstName + ", lastName=" + this.lastName + ")"; } public int hashCode() { return (this.firstName != null?this.firstName.hashCode():0) * 31 + (this.lastName != null?this.lastName.hashCode():0); } public boolean equals(Object var1) { if(this != var1) { if(var1 instanceof User) { User var2 = (User)var1; if(Intrinsics.areEqual(this.firstName, var2.firstName) && Intrinsics.areEqual(this.lastName, var2.lastName)) { return true; } } return false; } else { return true; } } }
  10. @TTGonda collectiveidea.com @NotNull public final String component1() { return this.firstName;

    } @Nullable public final String component2() { return this.lastName; } } public final void setLastName(@Nullable String this.lastName = var1; }
  11. @TTGonda collectiveidea.com @NotNull public final String component1() { return this.firstName;

    } @Nullable public final String component2() { return this.lastName; } } public final void setLastName(@Nullable String this.lastName = var1; } // Destructuring Data Class Declarations val user = User("Victoria", "Gonda") val (firstname, lastname) = user
  12. @TTGonda collectiveidea.com @NotNull public final String component1() { return this.firstName;

    } @Nullable public final String component2() { return this.lastName; } } public final void setLastName(@Nullable String this.lastName = var1; }
  13. @TTGonda collectiveidea.com @NotNull public final User copy( @NotNull String firstName,

    @Nullable String lastName) { Intrinsics .checkParameterIsNotNull(firstName, "firstName"); return new User(firstName, lastName); } return this.firstName; } @Nullable public final String component2() { return this.lastName; }
  14. @TTGonda collectiveidea.com // $FF: synthetic method // $FF: bridge method

    @NotNull public static User copy$default( User var0, String var1, String var2, int var3, Object var4) { if((var3 & 1) != 0) { var1 = var0.firstName; } if((var3 & 2) != 0) { var2 = var0.lastName; } return var0.copy(var1, var2); } public final User copy( @NotNull String firstName, @Nullable String lastName) { Intrinsics .checkParameterIsNotNull(firstName, "firstName"); return new User(firstName, lastName); }
  15. @TTGonda collectiveidea.com public String toString() { return "User(firstName=" + this.firstName

    + ", lastName=" + this.lastName + ")"; } if((var3 & 1) != 0) { var1 = var0.firstName; } if((var3 & 2) != 0) { var2 = var0.lastName; } return var0.copy(var1, var2); }
  16. @TTGonda collectiveidea.com public int hashCode() { return (this.firstName != null?

    this.firstName.hashCode():0) * 31 + (this.lastName != null? this.lastName.hashCode():0); } public String toString() { return "User(firstName=" + this.firstName + ", lastName=" + this.lastName + ")"; }
  17. @TTGonda collectiveidea.com public boolean equals(Object var1) { if(this != var1)

    { if(var1 instanceof User) { User var2 = (User)var1; if(Intrinsics.areEqual(this.firstName, var2.firstName) && Intrinsics.areEqual(this.lastName, var2.lastName)) { return true; } } return false; } else { return true; } } } this.firstName.hashCode():0) * 31 + (this.lastName != null? this.lastName.hashCode():0); }
  18. @TTGonda collectiveidea.com public final class User { @NotNull private final

    String firstName; @Nullable private String lastName; public User(@NotNull String firstName, @Nullable String lastName) { Intrinsics.checkParameterIsNotNull(firstName, "firstName"); super(); this.firstName = firstName; this.lastName = lastName; } @NotNull public final String getFirstName() { return this.firstName; } @Nullable public final String getLastName() { return this.lastName; } public final void setLastName(@Nullable String <set-?>) { this.lastName = <set-?>; } @NotNull public final String component1() { return this.firstName; } @Nullable public final String component2() { return this.lastName; } @NotNull public final User copy(@NotNull String firstName, @Nullable String lastName) { Intrinsics.checkParameterIsNotNull(firstName, "firstName"); return new User(firstName, lastName); } // $FF: synthetic method // $FF: bridge method @NotNull public static User copy$default(User var0, String var1, String var2, int var3, Object var4) { if((var3 & 1) != 0) { var1 = var0.firstName; } if((var3 & 2) != 0) { var2 = var0.lastName; } return var0.copy(var1, var2); } public String toString() { return "User(firstName=" + this.firstName + ", lastName=" + this.lastName + ")"; } public int hashCode() { return (this.firstName != null?this.firstName.hashCode():0) * 31 + (this.lastName != null?this.lastName.hashCode():0); } public boolean equals(Object var1) { if(this != var1) { if(var1 instanceof User) { User var2 = (User)var1; if(Intrinsics.areEqual(this.firstName, var2.firstName) && Intrinsics.areEqual(this.lastName, var2.lastName)) { return true; } } return false; } else { return true; } } }
  19. @TTGonda collectiveidea.com String maybeString = (String) null; if(maybeString != null)

    { String string = (String)maybeString; string.length(); } let
  20. @TTGonda collectiveidea.com String maybeString = (String)null; 
 if(maybeString != null)

    {
 return maybeString.length();
 } else {
 return 0; } Elvis Operator ?:
  21. @TTGonda collectiveidea.com class CopyPrinter(copier: Copy, printer: Print) : Copy by

    copier, Print by printer interface Copy { fun copy(page: Page): Page } interface Print { fun print(page: Page) }
  22. @TTGonda collectiveidea.com public final class CopyPrinter implements Copy, Print {

    // $FF: synthetic field private final Copy $$delegate_0; // $FF: synthetic field private final Print $$delegate_1; public CopyPrinter(@NotNull Copy copier, @NotNull Print printer) { Intrinsics.checkParameterIsNotNull(copier, "copier"); Intrinsics.checkParameterIsNotNull(printer, "printer"); super(); this.$$delegate_0 = copier; this.$$delegate_1 = printer; } @NotNull public Page copy(@NotNull Page page) { Intrinsics.checkParameterIsNotNull(page, "page"); return this.$$delegate_0.copy(page); } public void print(@NotNull Page page) { Intrinsics.checkParameterIsNotNull(page, "page"); this.$$delegate_1.print(page); } } public interface Copy { @NotNull Page copy(@NotNull Page var1); } public interface Print { void print(@NotNull Page var1); }
  23. @TTGonda collectiveidea.com // $FF: synthetic field private final Copy $$delegate_0;

    // $FF: synthetic field private final Print $$delegate_1; public final class CopyPrinter implements Copy, Print {
  24. @TTGonda collectiveidea.com public CopyPrinter( @NotNull Copy copier, @NotNull Print printer)

    { Intrinsics .checkParameterIsNotNull(copier, "copier"); Intrinsics .checkParameterIsNotNull(printer, "printer"); super(); this.$$delegate_0 = copier; this.$$delegate_1 = printer; } // $FF: synthetic field private final Copy $$delegate_0; // $FF: synthetic field private final Print $$delegate_1;
  25. @TTGonda collectiveidea.com @NotNull public Page copy(@NotNull Page page) { Intrinsics

    .checkParameterIsNotNull(page, "page"); return this.$$delegate_0.copy(page); } public void print(@NotNull Page page) { Intrinsics .checkParameterIsNotNull(page, "page"); this.$$delegate_1.print(page); } } super(); this.$$delegate_0 = copier; this.$$delegate_1 = printer; }
  26. @TTGonda collectiveidea.com public interface Copy { @NotNull Page copy(@NotNull Page

    var1); } public interface Print { void print(@NotNull Page var1); } public void print(@NotNull Page page) { Intrinsics .checkParameterIsNotNull(page, "page"); this.$$delegate_1.print(page); } }
  27. @TTGonda collectiveidea.com public final class CopyPrinter implements Copy, Print {

    // $FF: synthetic field private final Copy $$delegate_0; // $FF: synthetic field private final Print $$delegate_1; public CopyPrinter(@NotNull Copy copier, @NotNull Print printer) { Intrinsics.checkParameterIsNotNull(copier, "copier"); Intrinsics.checkParameterIsNotNull(printer, "printer"); super(); this.$$delegate_0 = copier; this.$$delegate_1 = printer; } @NotNull public Page copy(@NotNull Page page) { Intrinsics.checkParameterIsNotNull(page, "page"); return this.$$delegate_0.copy(page); } public void print(@NotNull Page page) { Intrinsics.checkParameterIsNotNull(page, "page"); this.$$delegate_1.print(page); } } public interface Copy { @NotNull Page copy(@NotNull Page var1); } public interface Print { void print(@NotNull Page var1); }
  28. @TTGonda collectiveidea.com public final class StringExtKt { @NotNull public static

    final String double( @NotNull String $receiver) { Intrinsics .checkParameterIsNotNull($receiver, "$receiver"); return $receiver + $receiver; } }
  29. @TTGonda collectiveidea.com StringExtKt.double("hello"); // -> "hellohello" public final class StringExtKt

    { @NotNull public static final String double( @NotNull String $receiver) { Intrinsics .checkParameterIsNotNull($receiver, "$r return $receiver + $receiver; } }
  30. @TTGonda collectiveidea.com @NotNull public static final Integer[] firstNSquares(int n) {

    Integer[] result$iv = new Integer[n]; int i$iv = 0; int var3 = n - 1; if(i$iv <= var3) { while(true) { Integer var9 = Integer.valueOf(i$iv * i$iv); result$iv[i$iv] = var9; if(i$iv == var3) { break; } ++i$iv; } } return (Integer[])((Object[])result$iv); }
  31. @TTGonda collectiveidea.com @NotNull public static Integer[] firstNSquares(int n) { Integer[]

    resultArray = new Integer[n]; int i = 0; int max = n - 1; if(i <= max) { while(true) { Integer square = i * i; resultArray[i] = square; if(i == max) { break; } ++i; } } return resultArray; }
  32. @TTGonda collectiveidea.com fun firstNSquares(n: Int): Array<Int> = Array(n, { i

    -> square(i + 1) }) // firstNSquares(3) // -> [1, 4, 9]
  33. @TTGonda collectiveidea.com @NotNull public static Integer[] firstNSquares(int n) { Integer[]

    resultArray = new Integer[n]; int i = 0; int max = n - 1; if(i <= max) { while(true) { Integer square = square(i+1); resultArray[i] = square; if(i == max) { break; } ++i; } } return resultArray; }
  34. @TTGonda collectiveidea.com inline fun beforeAndAfter( startString: String, function: (string: String)

    -> String) { print("Before: $startString") val after = function(startString) print("After: $after") }
  35. @TTGonda collectiveidea.com inline fun beforeAndAfter( startString: String, function: (string: String)

    -> String) { print("Before: $startString") val after = function(startString) print("After: $after") } public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
  36. @TTGonda collectiveidea.com inline fun beforeAndAfter( startString: String, function: (string: String)

    -> String) { print("Before: $startString") val after = function(startString) print("After: $after") }
  37. @TTGonda collectiveidea.com public final void beforeAndAfter( @NotNull String startString, @NotNull

    Function1 function) { Intrinsics .checkParameterIsNotNull(startString, "startString"); Intrinsics .checkParameterIsNotNull(function, "function"); String after = "Before: " + startString; System.out.print(after); after = (String)function.invoke(startString); String var4 = "After: " + after; System.out.print(var4); }
  38. @TTGonda collectiveidea.com public final void beforeAndAfter( @NotNull String startString, @NotNull

    Function1 function) { Intrinsics .checkParameterIsNotNull(startString, "startString"); Intrinsics .checkParameterIsNotNull(function, "function"); String after = "Before: " + startString; System.out.print(after); after = (String)function.invoke(startString); String var4 = "After: " + after; System.out.print(var4); } public interface Function1<in P1, out R> : Function<R> { public operator fun invoke(p1: P1): R }
  39. @TTGonda collectiveidea.com public final void beforeAndAfter( @NotNull String startString, @NotNull

    Function1 function) { Intrinsics .checkParameterIsNotNull(startString, "startString"); Intrinsics .checkParameterIsNotNull(function, "function"); String after = "Before: " + startString; System.out.print(after); after = (String)function.invoke(startString); String var4 = "After: " + after; System.out.print(var4); }
  40. @TTGonda collectiveidea.com fun example() { beforeAndAfter("hello", { string -> string

    + " world" }) } // Output “Before: hello" “After: hello world"
  41. @TTGonda collectiveidea.com public final void example() { String startString$iv =

    "hello"; String after$iv = "Before: " + startString$iv; System.out.print(after$iv); String string = (String)startString$iv; after$iv = (String)(string + " world"); string = "After: " + after$iv; System.out.print(string); }
  42. @TTGonda collectiveidea.com beforeAndAfter("hello", { string -> string + " world"

    }) 
 beforeAndAfter("hello", { it + " world" }) 
 beforeAndAfter("hello") { it + " world" } Lambda Parameters
  43. @TTGonda collectiveidea.com fun beforeAndAfter( startString: String, function: (string: String) ->

    String ) { print("Before: $startString") val after = function(startString) print("After: $after") }
  44. @TTGonda collectiveidea.com // Pseudocode for bytecode final class BytecodeClass extends

    Lambda implements Function1 { public void invoke(String string) { StringBuilder sb = new StringBuilder("hello"); sb.append(" world"); returnValue = sb.toString(); } static Function1 INSTANCE = new BytecodeClass(); }
  45. @TTGonda collectiveidea.com // Pseudocode for bytecode final class BytecodeClass extends

    Lambda implements Function1 { public void invoke(String string) { StringBuilder sb = new StringBuilder("hello"); sb.append(" world"); returnValue = sb.toString(); } static Function1 INSTANCE = new BytecodeClass(); } public final void example() { this.beforeAndAfter("hello", (Function1)null.INSTANCE); }
  46. @TTGonda collectiveidea.com // Pseudocode for bytecode final class BytecodeClass extends

    Lambda implements Function1 { public void invoke(String string) { StringBuilder sb = new StringBuilder("hello"); sb.append(" world"); returnValue = sb.toString(); } static Function1 INSTANCE = new BytecodeClass(); }
  47. @TTGonda collectiveidea.com Menu > Tools > Kotlin > Show Kotlin

    Bytecode or CMD+SHFT+A and search “Show Kotlin Bytecode”
  48. @TTGonda collectiveidea.com Code > Convert Java File to Kotlin File

    or CMD+SHFT+A and search “Convert Java File to Kotlin File”