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

Kotlin: Uncovered - 360|AnDev 2017

Kotlin: Uncovered - 360|AnDev 2017

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.

Victoria Gonda

July 09, 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 // $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); } val user1 = user.copy() val user2 = user.copy(firstName = "Tori") val user3 = user.copy(lastName = null) val user4 = user.copy(firstName = "Josh", lastName = "Kovach")
  16. @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); }
  17. @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); }
  18. @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 + ")"; }
  19. @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); }
  20. @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; } } }
  21. @TTGonda collectiveidea.com data class User( val firstName: String = "Victoria",

    var lastName: String? ) Default Values // $FF: synthetic method public DataUser(String var1, String var2, int var3, DefaultConstructorMarker var4) { if((var3 & 1) != 0) { var1 = "Victoria"; } this(var1, var2); }
  22. @TTGonda collectiveidea.com String maybeString = this.getString(); return maybeString != null

    ? Integer.valueOf(maybeString.length()) : null; Null Safe Scoping
  23. @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) }
  24. @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); }
  25. @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 {
  26. @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; } implements Copy, Print { // $FF: synthetic field private final Copy $$delegate_0; // $FF: synthetic field private final Print $$delegate_1;
  27. @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); } } Intrinsics.checkParameterIsNotNull(printer, "print super(); this.$$delegate_0 = copier; this.$$delegate_1 = printer; }
  28. @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); } }
  29. @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); }
  30. @TTGonda collectiveidea.com public final class StringExtKt { public static final

    boolean isEmpty( @Nullable String $receiver) { return $receiver == null || $receiver.length() == 0; } }
  31. @TTGonda collectiveidea.com StringExtKt.isEmpty(""); // true public final class StringExtKt {

    public static final boolean isEmpty( @Nullable String $receiver) { return $receiver == null || $receiver.l } }
  32. @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); }
  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 = i * i; resultArray[i] = square; if(i == max) { break; } ++i; } } return resultArray; }
  34. @TTGonda collectiveidea.com fun firstNSquares(n: Int): Array<Int> = Array(n, { i

    -> square(i + 1) }) // firstNSquares(3) // -> [1, 4, 9]
  35. @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; }
  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 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)
  38. @TTGonda collectiveidea.com inline fun beforeAndAfter( startString: String, function: (string: String)

    -> String ) { print("Before: $startString") val after = function(startString) print("After: $after") }
  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 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 }
  41. @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); }
  42. @TTGonda collectiveidea.com fun example() { beforeAndAfter("hello", { string -> string

    + " world" }) } // Output // “Before: hello" // “After: hello world"
  43. @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); }
  44. @TTGonda collectiveidea.com beforeAndAfter("hello", { string -> string + " world"

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

    String ) { print("Before: $startString") val after = function(startString) print("After: $after") }
  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 // 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); }
  48. @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); } // access flags 0x19 public final static ../../../Lambda$example$1; INSTANCE
  49. @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(); }
  50. @TTGonda collectiveidea.com Menu > Tools > Kotlin > Show Kotlin

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

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