Share
Embed
Kotlin: Uncovered - I/O Extended GR '17
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
May 17, 2017
Transcript
101 slides
-
@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 -
@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 -
@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; } } -
@TTGonda collectiveidea.com @NotNull private final String firstName; @Nullable private String
lastName; public final class User { -
@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; -
@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); } } -
@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) -
@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; -
@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; } -
@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; } } } -
@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; } -
@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 -
@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; } -
@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; } -
@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); } -
@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") -
@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); } -
@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); } -
@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 + ")"; } -
@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); } -
@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; } } } -
@TTGonda collectiveidea.com data class User( val firstName: String = "Victoria",
var lastName: String? ) Default Values -
@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); } -
@TTGonda collectiveidea.com data class User( val firstName: String = "Victoria",
var lastName: String? ) Default Values -
@TTGonda collectiveidea.com val user = User( lastName = "Gonda" )
Named Parameters -
@TTGonda collectiveidea.com String maybeString = this.getString(); if(maybeString != null) {
maybeString.length(); } -
@TTGonda collectiveidea.com String maybeString = this.getString(); if(maybeString == null) {
Intrinsics.throwNpe(); } maybeString.length(); -
@TTGonda collectiveidea.com val maybeString: String? = getString() return maybeString?.let {
// it == maybeString it.length } Null Safe Scoping -
@TTGonda collectiveidea.com String maybeString = this.getString(); return maybeString != null
? Integer.valueOf(maybeString.length()) : null; Null Safe Scoping -
@TTGonda collectiveidea.com String maybeString = this.getString(); return maybeString != null
? maybeString.length() : 0; Elvis Operator ?: -
@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) } -
@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); } -
@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 { -
@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; -
@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; } -
@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); } } -
@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); } -
@TTGonda collectiveidea.com // StringExt.kt fun String?.isEmpty(): Boolean { return this
== null || this.length == 0 } -
@TTGonda collectiveidea.com "".isEmpty() // true // StringExt.kt fun String?.isEmpty(): Boolean
{ return this == null || this.length } -
@TTGonda collectiveidea.com public final class StringExtKt { public static final
boolean isEmpty( @Nullable String $receiver) { return $receiver == null || $receiver.length() == 0; } } -
@TTGonda collectiveidea.com StringExtKt.isEmpty(""); // true public final class StringExtKt {
public static final boolean isEmpty( @Nullable String $receiver) { return $receiver == null || $receiver.l } } -
@TTGonda collectiveidea.com fun firstNSquares(n: Int): Array<Int> = Array(n, { i
-> i * i }) // firstNSquares(3) // -> [0, 1, 4] -
@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); } -
@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; } -
@TTGonda collectiveidea.com fun firstNSquares(n: Int): Array<Int> = Array(n, { i
-> square(i + 1) }) // firstNSquares(3) // -> [1, 4, 9] -
@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; } -
@TTGonda collectiveidea.com inline fun beforeAndAfter( startString: String, function: (string: String)
-> String ) { print("Before: $startString") val after = function(startString) print("After: $after") } -
@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) -
@TTGonda collectiveidea.com inline fun beforeAndAfter( startString: String, function: (string: String)
-> String ) { print("Before: $startString") val after = function(startString) print("After: $after") } -
@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); } -
@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 } -
@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); } -
@TTGonda collectiveidea.com fun example() { beforeAndAfter("hello", { string -> string
+ " world" }) } // Output // “Before: hello" // “After: hello world" -
@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); } -
@TTGonda collectiveidea.com beforeAndAfter("hello", { string -> string + " world"
}) beforeAndAfter("hello", { it + " world" }) beforeAndAfter("hello") { it + " world" } Lambda Parameters -
@TTGonda collectiveidea.com fun beforeAndAfter( startString: String, function: (string: String) ->
String ) { print("Before: $startString") val after = function(startString) print("After: $after") } -
@TTGonda collectiveidea.com public final void example() { this.beforeAndAfter("hello", (Function1)null.INSTANCE); }
// Output // “Before: hello” // “After: hello world” -
@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(); } -
@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); } -
@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 -
@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(); } -
@TTGonda collectiveidea.com Companion Objects Smart Casting Collection Functions Control Flow
Structures Operator Overloading Named Parameters -
@TTGonda collectiveidea.com Menu > Tools > Kotlin > Show Kotlin
Bytecode or CMD+SHFT+A and search “Show Kotlin Bytecode” -
@TTGonda collectiveidea.com Code > Convert Java File to Kotlin File
or CMD+SHFT+A and search “Convert Java File to Kotlin File”