In this talk, we'll dive into the details of how various language features supported by Kotlin are translated to Java bytecode. We'll use the JMH microbenchmarking tool to study the relative performance of various constructs and to understand how we can ensure top performance of the Kotlin code that we write.
8. Working with metadata
class A {
fun foo(): String? = null
fun bar(): String = ""
}
fun main(args: Array<String>) {
println(A::class.functions.filter {
it.returnType.isMarkedNullable
})
}
11. Primary constructors
class A(val x: Int) {
}
public final class A {
private final int x;
public final int getX() {
return this.x;
}
public A(int x) {
this.x = x;
}
}
12. Data classes
data class A(val x: Int) {
}
public final class A {
…
public String toString() {
return "A(x=" + this.x + ")";
}
public int hashCode() {
return this.x;
}
public boolean equals(Object o) {
…
}
}
13. Properties
class A {
var x: String? = null
}
public final class A {
@Nullable
private String x;
@Nullable
public final String getX() {
return this.x;
}
public final void setX(
@Nullable String x) {
this.x = x;
}
}
15. Not-null types
class A {
fun x(s: String) {
println(s)
}
private fun y(s: String) {
println(s)
}
}
public final class A {
public final void x(@NotNull String s) {
Intrinsics.
checkParameterIsNotNull(s, "s");
System.out.println(s);
}
private final void y(String s) {
System.out.println(s);
}
}
16. Parameter null checks
1 parameter
8 parameters
ns
0 2,25 4,5 6,75 9
Without @NotNull With @NotNull
17. Extension functions
class A(val i: Int)
fun A.foo(): Int {
return i
}
fun useFoo() {
A(1).foo()
}
public final class ExtFunKt {
public static final void foo(
@NotNull A $receiver) {
return $receiver.getI();
}
}
public static final void useFoo() {
foo(new A(1));
}
18. Interface methods
interface I {
fun foo(): Int {
return 42
}
}
class C : I {
}
public interface I {
int foo();
public static final class
DefaultImpls {
public static int foo(I $this) {
return 42;
}
}
}
public final class C implements I {
public void foo() {
I.DefaultImpls.foo(this);
}
}
19. Interface methods: Evolution
interface I {
fun foo(): Int {
return 42
}
fun bar(): Int {
return 239
}
}
// -- separate compilation ——————
class C : I {
}
public interface I {
int foo();
int bar();
public static final class
DefaultImpls { … }
}
// -- separate compilation ——————
public final class C implements I {
public void foo() {
I.DefaultImpls.foo(this);
}
}
20. Default Arguments
fun foo(x: Int = 42) {
println(x)
}
fun bar() {
foo()
}
public static final void foo(int x) {
System.out.println(x);
}
public static void foo$default(
int x, int mask, …) {
if ((mask & 1) != 0) {
x = 42;
}
foo(x);
}
public static final void bar() {
foo$default(0, 1, …);
}
22. @JvmOverloads
@JvmOverloads
fun foo(x: Int = 42) {
}
public static final void foo(int x)
{
}
public static void foo$default(
int x, int mask, …) { … }
public static void foo() {
foo$default(0, 1, …);
}
23. Lambdas: Functional types
fun <T> runLambda(x: () -> T): T =
x()
private static final
Object runLambda(Function0 x) {
return x.invoke();
}
package kotlin.jvm.functions
/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
/** Invokes the function. */
public operator fun invoke(): R
}
24. Lambdas: Noncapturing
var value = 0
fun noncapLambda(): Int
= runLambda { value }
final class LB$noncapLambda$1
extends Lambda implements Function0 {
public static final LB$noncapLambda$1
INSTANCE = new LB$noncapLambda$1();
public final int invoke() {
return LambdaBenchmarkKt.getValue();
}
}
public static int noncapLambda() {
return ((Number)runLambda(
LB$noncapLambda$1.INSTANCE)
).intValue();
}
25. Lambdas: Capturing
fun capturingLambda(v: Int): Int
= runLambda { v }
public static int
capturingLambda(int value) {
return ((Number)RL.runLambda(
new Function0(0) {
public final int invoke() {
return value;
}
})
).intValue();
26. Lambdas: Capture and mutate
fun mutatingLambda(): Int {
var x = 0
runLambda { x++ }
return x
}
public static int mutatingLambda()
{
final IntRef x = new IntRef();
x.element = 0;
RL.runLambda(new Function0(0) {
public final int invoke() {
int var1 = x.element++;
return var1;
}
});
return x.element;
}
public static final class IntRef {
public volatile int element;
@Override
public String toString() {
return String.valueOf(element);
}
}
28. Lambdas: inline
fun inlineLambda(x: Int): Int =
run { x }
public static int
inlineLambda(int x) {
return x;
}
/**
* Calls the specified function [block] and returns its result.
*/
public inline fun <R> run(block: () -> R): R = block()
29. Method References
private fun referenced() = 1
fun runReference() {
runLambda(::referenced)
}
final class MethodReferenceKt$runReference$1
extends FunctionReference implements Function0
{
public static final MethodReferenceKt
$runReference$1 INSTANCE = new
MethodReferenceKt$runReference$1();
public final int invoke() {
return MethodReferenceKt.access
$referenced();
}
public final KDeclarationContainer
getOwner() { … }
public final String getName() { … }
public final String getSignature() { … }
}
30. Loops: Range
fun rangeLoop() {
for (i in 1..10) {
println(i)
}
}
public static final void
rangeLoop() {
int i = 1;
byte var1 = 10;
if(i <= var1) {
while(true) {
System.out.println(i);
if(i == var1) {
break;
}
++i;
}
}
}
31. Loops: Array
fun arrayLoop(x: Array<String>) {
for (s in x) {
println(s)
}
}
public static void
arrayLoop(@NotNull String[] x) {
for(int var2 = 0;
var2 < x.length; ++var2) {
String s = x[var2];
System.out.println(s);
}
}
32. Loops: List
fun listLoop(x: List<String>) {
for (s in x) {
println(s)
}
}
public static final void
listLoop(@NotNull List x) {
Iterator var2 = x.iterator();
while(var2.hasNext()) {
String s =
(String)var2.next();
System.out.println(s);
}
}
35. When: Constants
val ZERO = 0
val ONE = 1
fun constWhen(x: Int): String =
when(x) {
ZERO -> "zero"
ONE -> "one"
else -> "many"
}
public static String constWhen(
int x) {
return x == ZERO ? “zero"
: (x == ONE ? “one" : "many");
}
36. When: enum
enum class NumberValue {
ZERO, ONE, MANY
}
fun enumWhen(x: NumberValue):
String = when(x) {
NumberValue.ZERO -> "zero"
NumberValue.ONE -> "one"
else -> "many"
}
public static String
enumWhen(@NotNull NumberValue x) {
String var10000;
switch(WhenEnumKt$WhenMappings.
$EnumSwitchMapping$0[x.ordinal()]) {
case 1:
var10000 = "zero";
break;
case 2:
var10000 = "one";
break;
default:
var10000 = "many";
}
return var10000;
}
38. Summary
• Kotlin allows writing code which is easy to use from Java
• Performance in most scenarios is on par with Java
• Inline functions 👍
• Measure the performance of your code, not micro-examples