source (.java) Java 8 File.class Compiler Javac source (.java, .kt) Java 8 File.class Compiler Javac Kotlinc decompiled java source Decompiler fernflower Any Int Long Double ... Number String Unit Nothing Array IntArray DoubleArray Pair Triple Boolean ...Array YourClass Any? Int? Long? Double? ...? Number? String? Unit? Nothing? Array? IntArray? DoubleArray? Pair? Triple? Boolean? ...Array? YourClass? Nothing source (.java, .kt) Java 8 File.class Compiler Transformers Desugar Proguard Javac Kotlinc ... Java 6/7 Optimized and/or Obfuscated File.class Dex File.dex R8 & D8 ... «interface» Iterable<T> «interface» MutableCollection<T> «interface» Collection<T> «interface» MutableIterable<T> «interface» Set<T> «interface» MutableList<T> «interface» List<T> «interface» MutableSet<T> «interface» Iterator<T> «interface» MutableIterator<T> Provide Provide «interface» Sequence<T> Provide «interface» DropTakeSequence<T> «interface» Map<K,V> «interface» MutableMap<K,V> «interface» MapWithDefault<K,V> «interface» MutableMapWithDefault<K,V>

Deep Dive Kotlin :
du Hello World au ByteCode

Deep Dive Kotlin :
du Hello World au ByteCode

Deep Dive Kotlin :
du Hello World au ByteCode

Speakers

Roadmap

  1. Introduction

  2. ByteCode Java ?

  3. Introduction Kotlin

  4. Les bases

  5. null-safety

  6. Les types

  7. Les fonctions

  8. Les lambdas

  9. Les classes

  10. Pause

  11. ByteCode Android

  12. Autres structures

  13. Extensions de fonctions

  14. Les collections

  15. Les delegates

  16. inline

  17. Coroutines

  18. Conclusion

ByteCode Java ?

ByteCode Java ?

javac

Java compiler

HelloWorld.java

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello RivieraDev");
    }
}
javac HelloWorld.java

Java ByteCode binary

hexdump -C HelloWorld.class
00000000  ca fe ba be 00 00 00 34  00 1d 0a 00 06 00 0f 09  |.......4........|
00000010  00 10 00 11 08 00 12 0a  00 13 00 14 07 00 15 07  |................|
00000020  00 16 01 00 06 3c 69 6e  69 74 3e 01 00 03 28 29  |.....<init>...()|
00000030  56 01 00 04 43 6f 64 65  01 00 0f 4c 69 6e 65 4e  |V...Code...LineN|
00000040  75 6d 62 65 72 54 61 62  6c 65 01 00 04 6d 61 69  |umberTable...mai|
00000050  6e 01 00 16 28 5b 4c 6a  61 76 61 2f 6c 61 6e 67  |n...([Ljava/lang|
00000060  2f 53 74 72 69 6e 67 3b  29 56 01 00 0a 53 6f 75  |/String;)V...Sou|
00000070  72 63 65 46 69 6c 65 01  00 0f 48 65 6c 6c 6f 57  |rceFile...HelloW|
00000080  6f 72 6c 64 2e 6a 61 76  61 0c 00 07 00 08 07 00  |orld.java.......|
00000090  17 0c 00 18 00 19 01 00  0c 48 65 6c 6c 6f 20 44  |.........Hello D|
000000a0  65 76 6f 78 78 07 00 1a  0c 00 1b 00 1c 01 00 19  |evoxx...........|
000000b0  5f 30 30 5f 68 65 6c 6c  6f 77 6f 72 6c 64 2f 48  |_00_helloworld/H|
000000c0  65 6c 6c 6f 57 6f 72 6c  64 01 00 10 6a 61 76 61  |elloWorld...java|
000000d0  2f 6c 61 6e 67 2f 4f 62  6a 65 63 74 01 00 10 6a  |/lang/Object...j|
000000e0  61 76 61 2f 6c 61 6e 67  2f 53 79 73 74 65 6d 01  |ava/lang/System.|
000000f0  00 03 6f 75 74 01 00 15  4c 6a 61 76 61 2f 69 6f  |..out...Ljava/io|
00000100  2f 50 72 69 6e 74 53 74  72 65 61 6d 3b 01 00 13  |/PrintStream;...|
00000110  6a 61 76 61 2f 69 6f 2f  50 72 69 6e 74 53 74 72  |java/io/PrintStr|
00000120  65 61 6d 01 00 07 70 72  69 6e 74 6c 6e 01 00 15  |eam...println...|
00000130  28 4c 6a 61 76 61 2f 6c  61 6e 67 2f 53 74 72 69  |(Ljava/lang/Stri|
00000140  6e 67 3b 29 56 00 21 00  05 00 06 00 00 00 00 00  |ng;)V.!.........|
00000150  02 00 01 00 07 00 08 00  01 00 09 00 00 00 1d 00  |................|
00000160  01 00 01 00 00 00 05 2a  b7 00 01 b1 00 00 00 01  |.......*........|
00000170  00 0a 00 00 00 06 00 01  00 00 00 03 00 09 00 0b  |................|
00000180  00 0c 00 01 00 09 00 00  00 25 00 02 00 01 00 00  |.........%......|
00000190  00 09 b2 00 02 12 03 b6  00 04 b1 00 00 00 01 00  |................|
000001a0  0a 00 00 00 0a 00 02 00  00 00 06 00 08 00 07 00  |................|
000001b0  01 00 0d 00 00 00 02 00  0e                       |.........|
000001b9

Explorons le ByteCode

javap -c HelloWorld.class
Compiled from "HelloWorld.java"
public class _00_helloworld.HelloWorld {
  public _00_helloworld.HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello RivieraDev
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

À propos du ByteCode

  • Environ 200 opérations possibles (maxi. 256 opscodes)
  • Préfixe pour le type d'opérations (i pour entier, d pour double, ...)

  • Manipulation de la pile, des variables locales (iconst_0, istore, iload, ...)

  • Contrôle du flux des instructions (if_icmpgt, goto, ...)

  • Arithmétique et conversion de type (iadd, iinc, i2d, ...)

  • Manipulation d'objets (invokevirtual, invokedynamic, ...)

  • Autres (athrow, ...)

Jouons un peu

Introduction Kotlin

Introduction Kotlin

Historique

  • 2011 - 2015

    Dévoilé par JetBrains

  • 2016

    v1.0

    Support annoncé par Spring

  • 2017

    v1.1 : coroutines, JS, ...

    Support par Google

    v1.2 : multiplatform

  • 2018

    v1.3

    Coroutine stable

    Kotlin Native beta

    Kotlin Foundation

  • 2019

    corountine-flow

    v1.4 ?

    ???

  • ...

Cibles

JVM
JVM
Android
Android
JavaScript
JavaScript
LLVM
LLVM

JVM et Android

JavaScript

Native avec LLVM

HelloWorld.kt

fun main() {
    println("Hello RivieraDev")
}
kotlinc HelloWorld.kt

kotlinc

kotlinc

hexdump

00000000  ca fe ba be 00 00 00 32  00 2b 01 00 0c 48 65 6c  |.......2.+...Hel|
00000010  6c 6f 57 6f 72 6c 64 4b  74 07 00 01 01 00 10 6a  |loWorldKt......j|
00000020  61 76 61 2f 6c 61 6e 67  2f 4f 62 6a 65 63 74 07  |ava/lang/Object.|
00000030  00 03 01 00 04 6d 61 69  6e 01 00 03 28 29 56 01  |.....main...()V.|
00000040  00 16 28 5b 4c 6a 61 76  61 2f 6c 61 6e 67 2f 53  |..([Ljava/lang/S|
00000050  74 72 69 6e 67 3b 29 56  0c 00 05 00 06 0a 00 02  |tring;)V........|
00000060  00 08 01 00 10 48 65 6c  6c 6f 20 52 69 76 69 65  |.....Hello Rivie|
00000070  72 61 44 65 76 08 00 0a  01 00 10 6a 61 76 61 2f  |raDev......java/|
00000080  6c 61 6e 67 2f 53 79 73  74 65 6d 07 00 0c 01 00  |lang/System.....|
00000090  03 6f 75 74 01 00 15 4c  6a 61 76 61 2f 69 6f 2f  |.out...Ljava/io/|
000000a0  50 72 69 6e 74 53 74 72  65 61 6d 3b 0c 00 0e 00  |PrintStream;....|
000000b0  0f 09 00 0d 00 10 01 00  13 6a 61 76 61 2f 69 6f  |.........java/io|
000000c0  2f 50 72 69 6e 74 53 74  72 65 61 6d 07 00 12 01  |/PrintStream....|
000000d0  00 07 70 72 69 6e 74 6c  6e 01 00 15 28 4c 6a 61  |..println...(Lja|
000000e0  76 61 2f 6c 61 6e 67 2f  4f 62 6a 65 63 74 3b 29  |va/lang/Object;)|
000000f0  56 0c 00 14 00 15 0a 00  13 00 16 01 00 11 4c 6b  |V.............Lk|
00000100  6f 74 6c 69 6e 2f 4d 65  74 61 64 61 74 61 3b 01  |otlin/Metadata;.|
00000110  00 02 6d 76 03 00 00 00  01 03 00 00 00 0f 01 00  |..mv............|
00000120  02 62 76 03 00 00 00 00  03 00 00 00 03 01 00 01  |.bv.............|
00000130  6b 03 00 00 00 02 01 00  02 64 31 01 00 13 c0 80  |k........d1.....|
00000140  06 0a c0 80 0a 02 10 02  1a 06 10 c0 80 1a 02 30  |...............0|
00000150  01 01 00 02 64 32 01 00  00 01 00 0d 48 65 6c 6c  |....d2......Hell|
00000160  6f 57 6f 72 6c 64 2e 6b  74 01 00 04 43 6f 64 65  |oWorld.kt...Code|
00000170  01 00 0f 4c 69 6e 65 4e  75 6d 62 65 72 54 61 62  |...LineNumberTab|
00000180  6c 65 01 00 0a 53 6f 75  72 63 65 46 69 6c 65 01  |le...SourceFile.|
00000190  00 14 53 6f 75 72 63 65  44 65 62 75 67 45 78 74  |..SourceDebugExt|
000001a0  65 6e 73 69 6f 6e 01 00  19 52 75 6e 74 69 6d 65  |ension...Runtime|
000001b0  56 69 73 69 62 6c 65 41  6e 6e 6f 74 61 74 69 6f  |VisibleAnnotatio|
000001c0  6e 73 00 31 00 02 00 04  00 00 00 00 00 02 00 19  |ns.1............|
000001d0  00 05 00 06 00 01 00 26  00 00 00 29 00 02 00 02  |.......&...)....|
000001e0  00 00 00 0d 12 0b 4b 03  3c b2 00 11 2a b6 00 17  |......K.<...*...|
000001f0  b1 00 00 00 01 00 27 00  00 00 0a 00 02 00 00 00  |......'.........|
00000200  02 00 0c 00 03 10 09 00  05 00 07 00 01 00 26 00  |..............&.|
00000210  00 00 10 00 00 00 01 00  00 00 04 b8 00 09 b1 00  |................|
00000220  00 00 00 00 03 00 28 00  00 00 02 00 25 00 29 00  |......(.....%.).|
00000230  00 00 54 53 4d 41 50 0a  48 65 6c 6c 6f 57 6f 72  |..TSMAP.HelloWor|
00000240  6c 64 2e 6b 74 0a 4b 6f  74 6c 69 6e 0a 2a 53 20  |ld.kt.Kotlin.*S |
00000250  4b 6f 74 6c 69 6e 0a 2a  46 0a 2b 20 31 20 48 65  |Kotlin.*F.+ 1 He|
00000260  6c 6c 6f 57 6f 72 6c 64  2e 6b 74 0a 48 65 6c 6c  |lloWorld.kt.Hell|
00000270  6f 57 6f 72 6c 64 4b 74  0a 2a 4c 0a 31 23 31 2c  |oWorldKt.*L.1#1,|
00000280  34 3a 31 0a 2a 45 0a 00  2a 00 00 00 3a 00 01 00  |4:1.*E..*...:...|
00000290  18 00 05 00 19 5b 00 03  49 00 1a 49 00 1a 49 00  |.....[..I..I..I.|
000002a0  1b 00 1c 5b 00 03 49 00  1a 49 00 1d 49 00 1e 00  |...[..I..I..I...|
000002b0  1f 49 00 20 00 21 5b 00  01 73 00 22 00 23 5b 00  |.I. .![..s.".#[.|
000002c0  02 73 00 05 73 00 24                              |.s..s.$|
000002c7

Java ByteCode

Compiled from "HelloWorld.kt"
public final class HelloWorldKt {
  public static final void main();
    Code:
       0: ldc           #11                 // String Hello RivieraDev
       2: astore_0
       3: iconst_0
       4: istore_1
       5: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
       8: aload_0
       9: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      12: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #9                  // Method main:()V
       3: return
}

HelloWorld-java

import kotlin.Metadata;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},
        d2 = {"main", "", "kotlinDee"}
)
public final class HelloWorldKt {
   public static final void main() {
      String var0 = "Hello RivieraDev";
      boolean var1 = false;
      System.out.println(var0);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

Bilan HelloWorld.kt

  • Kotlin ajoute du contenu
  • du coup, on a besoin de JARs en plus
jarversiontaille
kotlin-stdlib kotlin-stdlib1.3.311.3M
kotlin-stdlib-jdk7 kotlin-stdlib-jdk71.3.313.1k
kotlin-stdlib-jdk8 kotlin-stdlib-jdk81.3.3113k
kotlin-reflect kotlin-reflect1.3.312.8M
lombok lombok1.18.61.7M
spring-core spring-core5.1.61.3M
Taille des JAR

Les bases

Les bases

val-var.kt

var x: Int = 10
val y: Int = 3
x += 4
// y += 4 <== 💣 Compilation Error

println(x * y) // 42

string-template.kt

fun greeting(who: Someone) {
    println("Hello $who!")
    println("Hello ${who.firstName} ${who.lastName}!")
    println("""
        multi
        line "$who"
        string""".trimIndent())
}

string-template.java

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0003¨\u0006\u0004"},
        d2 = {"greeting", "", "who", "LSomeone;", "kotlinDee"}
)
public final class GreetingKt {
   public static final void greeting(@NotNull Someone who) {
      Intrinsics.checkParameterIsNotNull(who, "who");
      String var1 = "Hello " + who + '!';
      boolean var2 = false;
      System.out.println(var1);
      var1 = "Hello " + who.getFirstName() + ' ' + who.getLastName() + '!';
      var2 = false;
      System.out.println(var1);
      var1 = StringsKt.trimIndent("\n        multi\n        line \"" + who + "\"\n        string");
      var2 = false;
      System.out.println(var1);
   }
}

ByteCode de string-template

Compiled from "greeting.kt"
public final class GreetingKt {
  public static final void greeting(Someone);
    Code:
       0: aload_0
       1: ldc           #9                  // String who
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #17                 // class java/lang/StringBuilder
       9: dup
      10: invokespecial #21                 // Method java/lang/StringBuilder."<init>":()V
      13: ldc           #23                 // String Hello
      15: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: aload_0
      19: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      22: bipush        33
      24: invokevirtual #33                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      27: invokevirtual #37                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      30: astore_1
      31: iconst_0
      32: istore_2
      33: getstatic     #43                 // Field java/lang/System.out:Ljava/io/PrintStream;
      36: aload_1
      37: invokevirtual #49                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      40: new           #17                 // class java/lang/StringBuilder
      43: dup
      44: invokespecial #21                 // Method java/lang/StringBuilder."<init>":()V
      47: ldc           #23                 // String Hello
      49: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      52: aload_0
      53: invokevirtual #54                 // Method Someone.getFirstName:()Ljava/lang/String;
      56: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      59: bipush        32
      61: invokevirtual #33                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      64: aload_0
      65: invokevirtual #57                 // Method Someone.getLastName:()Ljava/lang/String;
      68: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      71: bipush        33
      73: invokevirtual #33                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      76: invokevirtual #37                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      79: astore_1
      80: iconst_0
      81: istore_2
      82: getstatic     #43                 // Field java/lang/System.out:Ljava/io/PrintStream;
      85: aload_1
      86: invokevirtual #49                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      89: new           #17                 // class java/lang/StringBuilder
      92: dup
      93: invokespecial #21                 // Method java/lang/StringBuilder."<init>":()V
      96: ldc           #59                 // String \n        multi\n        line \"
      98: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     101: aload_0
     102: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
     105: ldc           #61                 // String \"\n        string
     107: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     110: invokevirtual #37                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     113: invokestatic  #67                 // Method kotlin/text/StringsKt.trimIndent:(Ljava/lang/String;)Ljava/lang/String;
     116: astore_1
     117: iconst_0
     118: istore_2
     119: getstatic     #43                 // Field java/lang/System.out:Ljava/io/PrintStream;
     122: aload_1
     123: invokevirtual #49                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     126: return
}

numeric.kt

val anInt = 42  // type inference: Int
val aLong = 42L // type inference: Long
var aDouble: Double? = null

numeric.java

import kotlin.Metadata;
import org.jetbrains.annotations.Nullable;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u001a\n\u0000\n\u0002\u0010\u0006\n\u0002\b\u0006\n\u0002\u0010\t\n\u0002\b\u0003\n\u0002\u0010\b\n\u0002\b\u0003\"\u001e\u0010\u0000\u001a\u0004\u0018\u00010\u0001X\u0086\u000e¢\u0006\u0010\n\u0002\u0010\u0006\u001a\u0004\b\u0002\u0010\u0003\"\u0004\b\u0004\u0010\u0005\"\u0014\u0010\u0007\u001a\u00020\bX\u0086D¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n\"\u0014\u0010\u000b\u001a\u00020\fX\u0086D¢\u0006\b\n\u0000\u001a\u0004\b\r\u0010\u000e¨\u0006\u000f"},
        d2 = {"aDouble", "", "getADouble", "()Ljava/lang/Double;", "setADouble", "(Ljava/lang/Double;)V", "Ljava/lang/Double;", "aLong", "", "getALong", "()J", "anInt", "", "getAnInt", "()I", "kotlinDee"}
)
public final class NumericKt {
   private static final int anInt = 42;
   private static final long aLong = 42L;
   @Nullable
   private static Double aDouble;

   public static final int getAnInt() {
      return anInt;
   }

   public static final long getALong() {
      return aLong;
   }

   @Nullable
   public static final Double getADouble() {
      return aDouble;
   }

   public static final void setADouble(@Nullable Double var0) {
      aDouble = var0;
   }
}

ByteCode de numeric

Compiled from "numeric.kt"
public final class NumericKt {
  public static final int getAnInt();
    Code:
       0: getstatic     #11                 // Field anInt:I
       3: ireturn

  public static final long getALong();
    Code:
       0: getstatic     #19                 // Field aLong:J
       3: lreturn

  public static final java.lang.Double getADouble();
    Code:
       0: getstatic     #26                 // Field aDouble:Ljava/lang/Double;
       3: areturn

  public static final void setADouble(java.lang.Double);
    Code:
       0: aload_0
       1: putstatic     #26                 // Field aDouble:Ljava/lang/Double;
       4: return

  static {};
    Code:
       0: bipush        42
       2: putstatic     #11                 // Field anInt:I
       5: ldc2_w        #14                 // long 42l
       8: putstatic     #19                 // Field aLong:J
      11: return
}

C'est simple

  • Plus de ; *

  • 😍 String templating
  • 😘 Plus de types primitifs (avant la compilation)
  • 🧐 Inférence de type
  • 🤝 On peut mélanger du code Java et Kotlin

null-safety

null-safety

billion-dollar mistake

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. [...] This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Tony Hoare (C.A.R. Hoare)
Null References: The Billion Dollar Mistake

null-safety.kt

fun main() {
    val somethingNotNull: String = "aString"
    // somethingNotNull: String = null => compilation error

    var length = somethingNotNull.length

    var something: String? = null
    length = something?.length ?: 0
    length = createSomething()?.length ?: 0

    // length = createSomething()!!.length // throw kotlin.KotlinNullPointerException

    // SmartCast
    something = "aString"
    length = something.length // auto cast to String (no need of `?.`)
}

fun createSomething(): String? = null

null-safety.java

import kotlin.Metadata;
import org.jetbrains.annotations.Nullable;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u000e\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\b\u0010\u0000\u001a\u0004\u0018\u00010\u0001\u001a\u0006\u0010\u0002\u001a\u00020\u0003¨\u0006\u0004"},
        d2 = {"createSomething", "", "main", "", "kotlinDee"}
)
public final class Null_safetyKt {
   public static final void main() {
      String somethingNotNull = "aString";
      int length = somethingNotNull.length();
      String something = (String)null;
      int length = false;
      String var10000 = createSomething();
      length = var10000 != null ? var10000.length() : 0;
      something = "aString";
      length = something.length();
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   @Nullable
   public static final String createSomething() {
      return null;
   }
}

Bilan null-safety

Les types

Les types

Types basiques

Types de base de Kotlin

Types basiques nullable

Types de base de Kotlin nullable

todo.kt

fun `is P = NP`(): Boolean =
    TODO()

fun main(args: Array<String>) {
    println("P = NP is ${`is P = NP`()}")
}

Hiérarchie des types

  • 🤝 le TODO() est l'ami du TDD

Les fonctions

Les fonctions

named-params.kt

fun buildString(prefix: String,
                who: String,
                enhanced: Boolean): String {
    var msg = "$prefix $who"
    if (enhanced) {
        msg += '!'
    }
    return msg
}

fun greetings(): String =
    buildString(enhanced = true, who = "RivieraDev", prefix = "Hello")

named-params.java

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u0012\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0003\n\u0002\u0010\u000b\n\u0002\b\u0002\u001a\u001e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u00012\u0006\u0010\u0003\u001a\u00020\u00012\u0006\u0010\u0004\u001a\u00020\u0005\u001a\u0006\u0010\u0006\u001a\u00020\u0001¨\u0006\u0007"},
        d2 = {"buildString", "", "prefix", "who", "enhanced", "", "greetings", "kotlinDee"}
)
public final class Named_paramsKt {
   @NotNull
   public static final String buildString(@NotNull String prefix, @NotNull String who, boolean enhanced) {
      Intrinsics.checkParameterIsNotNull(prefix, "prefix");
      Intrinsics.checkParameterIsNotNull(who, "who");
      String msg = prefix + ' ' + who;
      if (enhanced) {
         msg = msg + '!';
      }

      return msg;
   }

   @NotNull
   public static final String greetings() {
      String var0 = "Hello";
      String var1 = "RivieraDev";
      boolean var2 = true;
      return buildString(var0, var1, var2);
   }
}

default-value.kt

fun buildString2(prefix: String = "Hello",
                 who: String,
                 enhanced: Boolean = true): String {
    var msg = "$prefix $who"
    if (enhanced) {
        msg += '!'
    }
    return msg
}

fun greetings2(): String =
    buildString2(who = "RivieraDev")

Comment ça marche

// In my-app.jar
fun main() {
    println(compute())
}

// In lib-v1.0.0
fun compute(result: Int = 1) = result

// In lib-v1.0.1
fun compute(result: Int = 42) = result
  • Compile my-app avec lib-v1.0.0

  • java my-app.jar -cp lib-v1.0.0.jar

  • java my-app.jar -cp lib-v1.0.1.jar

  • Résultat ?

default-value.java

import kotlin.Metadata;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},
        d2 = {"main", "", "kotlinDee"}
)
public final class My_appKt {
    public static final void main() {
        int var0 = LibKt.compute$default(0, 1, (Object)null);
        boolean var1 = false;
        System.out.println(var0);
    }

    // $FF: synthetic method
    public static void main(String[] var0) {
        main();
    }
}

ByteCode de default-value

import kotlin.Metadata;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\n\n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\u001a\u0010\u0010\u0000\u001a\u00020\u00012\b\b\u0002\u0010\u0002\u001a\u00020\u0001¨\u0006\u0003"},
        d2 = {"compute", "", "result", "kotlinDee"}
)
public final class LibKt {
    public static final int compute(int result) {
        return result;
    }

    // $FF: synthetic method
    public static int compute$default(int var0, int var1, Object var2) {
        if ((var1 & 1) != 0) {
            var0 = 42;
        }

        return compute(var0);
    }
}

Kotlin c'est fun !

  • Toujours typer le retour de vos fonctions
  • (sauf si c'est évident et une surcharge comme le toString)

  • Kotlin est plus concis que Java
  • => évitez de faire des fonctions trop longues
  • Sautez une ligne après le =

    Utilisez le passage des arguments par nom quand ça lève des ambiguïtés

Les lambdas

Les lambdas

function.kt

// Declare apply function with function as parameter
fun compute(x: Int, y: Int, operation: (Int, Int) -> Int): Int =
    operation(x, y)

// Declare function
fun sumf(x: Int, y: Int): Int =
    x + y

// call compute with function reference
val sum5 = compute(2, 3, ::sumf)

// store function reference
val sumLam = ::sumf

// call compute with the function reference
val sum6 = compute(1, 5, sumLam)

function.java

import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import kotlin.reflect.KFunction;
import org.jetbrains.annotations.NotNull;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u001e\n\u0000\n\u0002\u0010\b\n\u0002\b\u0005\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0007\n\u0002\u0018\u0002\n\u0002\b\u0002\u001a0\u0010\u000e\u001a\u00020\u00012\u0006\u0010\n\u001a\u00020\u00012\u0006\u0010\u000b\u001a\u00020\u00012\u0018\u0010\u000f\u001a\u0014\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\u00010\u0010\u001a\u0016\u0010\u0011\u001a\u00020\u00012\u0006\u0010\n\u001a\u00020\u00012\u0006\u0010\u000b\u001a\u00020\u0001\"\u0011\u0010\u0000\u001a\u00020\u0001¢\u0006\b\n\u0000\u001a\u0004\b\u0002\u0010\u0003\"\u0011\u0010\u0004\u001a\u00020\u0001¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0003\"A\u0010\u0006\u001a2\u0012\u0013\u0012\u00110\u0001¢\u0006\f\b\b\u0012\b\b\t\u0012\u0004\b\b(\n\u0012\u0013\u0012\u00110\u0001¢\u0006\f\b\b\u0012\b\b\t\u0012\u0004\b\b(\u000b\u0012\u0004\u0012\u00020\u00010\u0007¢\u0006\b\n\u0000\u001a\u0004\b\f\u0010\r¨\u0006\u0012"},
        d2 = {"sum5", "", "getSum5", "()I", "sum6", "getSum6", "sumLam", "Lkotlin/reflect/KFunction2;", "Lkotlin/ParameterName;", "name", "x", "y", "getSumLam", "()Lkotlin/reflect/KFunction;", "compute", "operation", "Lkotlin/Function2;", "sumf", "kotlinDee"}
)
public final class FunctionKt {
   private static final int sum5;
   @NotNull
   private static final KFunction sumLam;
   private static final int sum6;

   public static final int compute(int x, int y, @NotNull Function2 operation) {
      Intrinsics.checkParameterIsNotNull(operation, "operation");
      return ((Number)operation.invoke(x, y)).intValue();
   }

   public static final int sumf(int x, int y) {
      return x + y;
   }

   public static final int getSum5() {
      return sum5;
   }

   @NotNull
   public static final KFunction getSumLam() {
      return sumLam;
   }

   public static final int getSum6() {
      return sum6;
   }

   static {
      sum5 = compute(2, 3, (Function2)null.INSTANCE);
      sumLam = null.INSTANCE;
      sum6 = compute(1, 5, (Function2)sumLam);
   }
}

lambda.kt

// Declare lambda
val suml: (Int, Int) -> Int = { x: Int, y: Int ->
    x + y
}

// call compute with suml lambda
val sum3 = compute(1, 2, suml)

// call compute with lamda
val sum4 = compute(1, 3) { x, y ->
    x + y
}

lambda.java

import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.NotNull;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u0012\n\u0000\n\u0002\u0010\b\n\u0002\b\u0005\n\u0002\u0018\u0002\n\u0002\b\u0003\"\u0011\u0010\u0000\u001a\u00020\u0001¢\u0006\b\n\u0000\u001a\u0004\b\u0002\u0010\u0003\"\u0011\u0010\u0004\u001a\u00020\u0001¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0003\"#\u0010\u0006\u001a\u0014\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\u00010\u0007¢\u0006\b\n\u0000\u001a\u0004\b\b\u0010\t¨\u0006\n"},
        d2 = {"sum3", "", "getSum3", "()I", "sum4", "getSum4", "suml", "Lkotlin/Function2;", "getSuml", "()Lkotlin/jvm/functions/Function2;", "kotlinDee"}
)
public final class LambdaKt {
   @NotNull
   private static final Function2 suml;
   private static final int sum3;
   private static final int sum4;

   @NotNull
   public static final Function2 getSuml() {
      return suml;
   }

   public static final int getSum3() {
      return sum3;
   }

   public static final int getSum4() {
      return sum4;
   }

   static {
      suml = (Function2)null.INSTANCE;
      sum3 = FunctionKt.compute(1, 2, suml);
      sum4 = FunctionKt.compute(1, 3, (Function2)null.INSTANCE);
   }
}

let.kt

val other = sumf(1, 2)
    .let { it + 1 }

val nullable = maybeAnInt()
    ?.let { it + 1 }

let.java

import kotlin.Metadata;
import org.jetbrains.annotations.Nullable;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\n\n\u0000\n\u0002\u0010\b\n\u0002\b\u0007\"\u0015\u0010\u0000\u001a\u0004\u0018\u00010\u0001¢\u0006\n\n\u0002\u0010\u0004\u001a\u0004\b\u0002\u0010\u0003\"\u0011\u0010\u0005\u001a\u00020\u0001¢\u0006\b\n\u0000\u001a\u0004\b\u0006\u0010\u0007¨\u0006\b"},
        d2 = {"nullable", "", "getNullable", "()Ljava/lang/Integer;", "Ljava/lang/Integer;", "other", "getOther", "()I", "kotlinDee"}
)
public final class LetKt {
   private static final int other;
   @Nullable
   private static final Integer nullable;

   public static final int getOther() {
      return other;
   }

   @Nullable
   public static final Integer getNullable() {
      return nullable;
   }

   static {
      int var0 = FunctionKt.sumf(1, 2);
      boolean var1 = false;
      boolean var2 = false;
      int var4 = false;
      other = var0 + 1;
      Integer var10000 = PlopKt.maybeAnInt();
      if (var10000 != null) {
         Integer var5 = var10000;
         var1 = false;
         var2 = false;
         int it = ((Number)var5).intValue();
         var4 = false;
         var10000 = it + 1;
      } else {
         var10000 = null;
      }

      nullable = var10000;
   }
}

Les lambdas

Les classes

Les classes

astronomy.kt

interface AstronomicalBody {
    val name: String
}

data class Planet(override val name: String,
                  val moons: List<Moon> = emptyList()) : AstronomicalBody {
    init {
        require(name.isNotEmpty())
    }

    operator fun plus(moon: Moon): Planet {
        return this.copy(moons = moons + moon)
    }
}

data class Moon(override val name: String) : AstronomicalBody

object SolarSystem {
    val moon = Moon(name = "Moon")
    val earth = Planet(name = "Earth") + moon

    val bodies: List<AstronomicalBody> = listOf(
        earth,
        Planet(name = "Jupiter")
    )
}

enum class PlanetKind {
    Terrestrial, GasGiant, IceGiant;

    companion object {
        fun fromName(name: String): PlanetKind {
            return PlanetKind.valueOf(name) // enumValueOf(name)
        }
    }
}

fun getMoons(planet: Planet): List<Moon> {
    val (_, moons) = planet
    return moons
}

astronomy.java

import java.util.Collection;
import java.util.List;
import kotlin.Metadata;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 1,
        d1 = {"\u00000\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010 \n\u0002\u0018\u0002\n\u0002\b\t\n\u0002\u0010\u000b\n\u0000\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\b\n\u0002\b\u0004\b\u0086\b\u0018\u00002\u00020\u0001B\u001d\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u000e\b\u0002\u0010\u0004\u001a\b\u0012\u0004\u0012\u00020\u00060\u0005¢\u0006\u0002\u0010\u0007J\t\u0010\f\u001a\u00020\u0003HÆ\u0003J\u000f\u0010\r\u001a\b\u0012\u0004\u0012\u00020\u00060\u0005HÆ\u0003J#\u0010\u000e\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\u000e\b\u0002\u0010\u0004\u001a\b\u0012\u0004\u0012\u00020\u00060\u0005HÆ\u0001J\u0013\u0010\u000f\u001a\u00020\u00102\b\u0010\u0011\u001a\u0004\u0018\u00010\u0012HÖ\u0003J\t\u0010\u0013\u001a\u00020\u0014HÖ\u0001J\u0011\u0010\u0015\u001a\u00020\u00002\u0006\u0010\u0016\u001a\u00020\u0006H\u0086\u0002J\t\u0010\u0017\u001a\u00020\u0003HÖ\u0001R\u0017\u0010\u0004\u001a\b\u0012\u0004\u0012\u00020\u00060\u0005¢\u0006\b\n\u0000\u001a\u0004\b\b\u0010\tR\u0014\u0010\u0002\u001a\u00020\u0003X\u0096\u0004¢\u0006\b\n\u0000\u001a\u0004\b\n\u0010\u000b¨\u0006\u0018"},
        d2 = {"LPlanet;", "LAstronomicalBody;", "name", "", "moons", "", "LMoon;", "(Ljava/lang/String;Ljava/util/List;)V", "getMoons", "()Ljava/util/List;", "getName", "()Ljava/lang/String;", "component1", "component2", "copy", "equals", "", "other", "", "hashCode", "", "plus", "moon", "toString", "kotlinDee"}
)
public final class Planet implements AstronomicalBody {
   @NotNull
   private final String name;
   @NotNull
   private final List moons;

   @NotNull
   public final Planet plus(@NotNull Moon moon) {
      Intrinsics.checkParameterIsNotNull(moon, "moon");
      return copy$default(this, (String)null, CollectionsKt.plus((Collection)this.moons, moon), 1, (Object)null);
   }

   @NotNull
   public String getName() {
      return this.name;
   }

   @NotNull
   public final List getMoons() {
      return this.moons;
   }

   public Planet(@NotNull String name, @NotNull List moons) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      Intrinsics.checkParameterIsNotNull(moons, "moons");
      super();
      this.name = name;
      this.moons = moons;
      CharSequence var3 = (CharSequence)this.getName();
      boolean var4 = false;
      boolean var8 = var3.length() > 0;
      var4 = false;
      boolean var5 = false;
      var5 = false;
      boolean var6 = false;
      if (!var8) {
         boolean var7 = false;
         String var9 = "Failed requirement.";
         throw (Throwable)(new IllegalArgumentException(var9.toString()));
      }
   }

   // $FF: synthetic method
   public Planet(String var1, List var2, int var3, DefaultConstructorMarker var4) {
      if ((var3 & 2) != 0) {
         var2 = CollectionsKt.emptyList();
      }

      this(var1, var2);
   }

   @NotNull
   public final String component1() {
      return this.getName();
   }

   @NotNull
   public final List component2() {
      return this.moons;
   }

   @NotNull
   public final Planet copy(@NotNull String name, @NotNull List moons) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      Intrinsics.checkParameterIsNotNull(moons, "moons");
      return new Planet(name, moons);
   }

   // $FF: synthetic method
   @NotNull
   public static Planet copy$default(Planet var0, String var1, List var2, int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = var0.getName();
      }

      if ((var3 & 2) != 0) {
         var2 = var0.moons;
      }

      return var0.copy(var1, var2);
   }

   @NotNull
   public String toString() {
      return "Planet(name=" + this.getName() + ", moons=" + this.moons + ")";
   }

   public int hashCode() {
      String var10000 = this.getName();
      int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;
      List var10001 = this.moons;
      return var1 + (var10001 != null ? var10001.hashCode() : 0);
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof Planet) {
            Planet var2 = (Planet)var1;
            if (Intrinsics.areEqual(this.getName(), var2.getName()) && Intrinsics.areEqual(this.moons, var2.moons)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

Héritage en Kotlin

open class SmallBody {
    open fun sizeRange(): IntRange = 0..10
}

// Inherit SmallBody
data class Comet(val name: String): SmallBody()

// Inherit SmallBody
data class Asteroid(val name: String): SmallBody() {
    override fun sizeRange(): IntRange = 0..4
}

fun main(args: Array<String>) {
    val bodies = listOf(Comet("Halley"), Asteroid("Adeona"))

    bodies.forEach { body ->
        println("$body: ${body.sizeRange()}")
    }
}

Génériques

  • ⚠️ Les contrôles de types génériques ne sont faits qu'au moment de la compilation
  • Covariant: out, en java ? extends T
  • Contravariant: in, en java ? super T
  • Projection * correspondant à Any? ou Nothing

Borne supérieure

fun <T : Comparable<T>> sort(list: List<T>): List<T>

Les détails: https://kotlinlang.org/docs/reference/generics.html

Sealed

sealed class JsonValue

data class JsonObject(val attributes: Map<String, JsonValue>) : JsonValue()
data class JsonArray(val values: List<JsonValue>) : JsonValue()
data class JsonString(val value: String) : JsonValue()
data class JsonNumber(val value: Number) : JsonValue()
data class JsonBoolean(val value: Boolean) : JsonValue()
object JsonNull : JsonValue()

Alias en Kotlin

interface Entity

typealias Id = String
typealias Version = Int
typealias EntityKey = Pair<Id, Version>
typealias Entities = List<Entity>

// fun getAllEntities(): Map<Pair<String, Int>, List<Entity>> = emptyMap()
fun getAllEntities(): Map<EntityKey, Entities> = emptyMap()

ByteCode d'alias

Compiled from "typealias.kt"
public final class TypealiasKt {
  public static final java.util.Map<kotlin.Pair<java.lang.String, java.lang.Integer>, java.util.List<Entity>> getAllEntities();
    Code:
       0: invokestatic  #13                 // Method kotlin/collections/MapsKt.emptyMap:()Ljava/util/Map;
       3: areturn
}

Classe, le bilan

  • 🤩 data class

  • 🤔 Mais pourquoi on n'a pas ça en Java ?

  • JEP draft: Records and Sealed Types
  • Une seule classe par fichier n'est pas utile

  • 🤓 sealed permet de faire des types algébriques de données (Algebraic Data Type)

Pause

Pause

ByteCode Android

ByteCode Android

Compilation pour Android

Compile Android

Dalvik EXecutable format

Dalvik Executable format
java -jar ./scripts/lib/d8.jar --release \
            --output ./target/android/dex \
            ./target/android/hello.jar
00000000  64 65 78 0a 30 33 35 00  06 50 f0 61 50 10 7c c0  |dex.035..P.aP.|.|
00000010  b2 f9 77 2d 54 df 60 f3  ac dd b0 10 eb 55 53 e1  |..w-T.`......US.|
00000020  28 f6 10 00 70 00 00 00  78 56 34 12 00 00 00 00  |(...p...xV4.....|
00000030  00 00 00 00 4c f5 10 00  12 17 00 00 70 00 00 00  |....L.......p...|
00000040  31 03 00 00 b8 5c 00 00  2a 08 00 00 7c 69 00 00  |1....\..*...|i..|
00000050  30 03 00 00 74 cb 00 00  20 1a 00 00 f4 e4 00 00  |0...t... .......|
00000060  75 02 00 00 f4 b5 01 00  94 f1 0e 00 94 04 02 00  |u...............|
00000070  4a f1 07 00 4c f1 07 00  75 f1 07 00 a4 f1 07 00  |J...L...u.......|
00000080  c2 f1 07 00 e0 f1 07 00  00 f2 07 00 23 f2 07 00  |............#...|
00000090  71 f2 07 00 b9 f2 07 00  07 f3 07 00 37 f3 07 00  |q...........7...|
000000a0  69 f3 07 00 90 f3 07 00  bc f3 07 00 e3 f3 07 00  |i...............|
000000b0  27 f4 07 00 71 f4 07 00  8f f4 07 00 ad f4 07 00  |'...q...........|
000000c0  cb f4 07 00 ed f4 07 00  0f f5 07 00 2c f5 07 00  |............,...|
000000d0  49 f5 07 00 79 f5 07 00  b1 f5 07 00 e9 f5 07 00  |I...y...........|
000000e0  1a f6 07 00 51 f6 07 00  7b f6 07 00 a6 f6 07 00  |....Q...{.......|
000000f0  d1 f6 07 00 0a f7 07 00  47 f7 07 00 84 f7 07 00  |........G.......|
00000100  c1 f7 07 00 02 f8 07 00  43 f8 07 00 84 f8 07 00  |........C.......|
00000110  f1 f8 07 00 12 f9 07 00  41 f9 07 00 6e f9 07 00  |........A...n...|
00000120  b0 f9 07 00 dd f9 07 00  0a fa 07 00 39 fa 07 00  |............9...|
00000130  68 fa 07 00 ab fa 07 00  e0 fa 07 00 25 fb 07 00  |h...........%...|
00000140  f3 fb 07 00 3b fc 07 00  79 fc 07 00 b1 fc 07 00  |....;...y.......|
00000150  eb fc 07 00 26 fd 07 00  68 fd 07 00 9e fd 07 00  |....&...h.......|
00000160  e7 fd 07 00 2a fe 07 00  5c fe 07 00 9d fe 07 00  |....*...\.......|
00000170  de fe 07 00 25 ff 07 00  5b ff 07 00 90 ff 07 00  |....%...[.......|
00000180  c0 ff 07 00 fc ff 07 00  75 00 08 00 ca 00 08 00  |........u.......|
00000190  30 01 08 00 a7 01 08 00  2f 02 08 00 c9 02 08 00  |0......./.......|
000001a0  74 03 08 00 30 04 08 00  fd 04 08 00 db 05 08 00  |t...0...........|
000001b0  ca 06 08 00 ca 07 08 00  db 08 08 00 fd 09 08 00  |................|
000001c0  30 0b 08 00 76 0c 08 00  cd 0d 08 00 35 0f 08 00  |0...v.......5...|
000001d0  ae 10 08 00 38 12 08 00  d3 13 08 00 7f 15 08 00  |....8...........|
000001e0  3c 17 08 00 7b 17 08 00  b8 17 08 00 f5 17 08 00  |<...{...........|
000001f0  34 18 08 00 80 18 08 00  cc 18 08 00 18 19 08 00  |4...............|
00000200  64 19 08 00 b0 19 08 00  fc 19 08 00 48 1a 08 00  |d...........H...|
00000210  94 1a 08 00 c8 1a 08 00  ff 1a 08 00 3f 1b 08 00  |............?...|
00000220  87 1b 08 00 62 1c 08 00  a8 1c 08 00 16 1d 08 00  |....b...........|
00000230  57 1d 08 00 a4 1d 08 00  e8 1d 08 00 31 1e 08 00  |W...........1...|
00000240  86 1e 08 00 d1 1e 08 00  23 1f 08 00 7a 1f 08 00  |........#...z...|
00000250  bf 1f 08 00 06 20 08 00  4d 20 08 00 f8 20 08 00  |..... ..M ... ..|
00000260  4f 21 08 00 84 21 08 00  b9 21 08 00 f8 21 08 00  |O!...!...!...!..|
00000270  4e 22 08 00 9d 22 08 00  e7 22 08 00 21 23 08 00  |N"..."..."..!#..|
00000280  6b 23 08 00 d2 23 08 00  0e 24 08 00 4a 24 08 00  |k#...#...$..J$..|
00000290  8d 24 08 00 e1 24 08 00  43 25 08 00 c6 25 08 00  |.$...$..C%...%..|
000002a0  14 26 08 00 6c 26 08 00  b9 26 08 00 0c 27 08 00  |.&..l&...&...'..|
000002b0  61 27 08 00 9f 27 08 00  e3 27 08 00 55 28 08 00  |a'...'...'..U(..|
000002c0  d1 28 08 00 2a 29 08 00  db 29 08 00 8c 2a 08 00  |.(..*)...)...*..|
000002d0  e5 2a 08 00 3e 2b 08 00  97 2b 08 00 e6 2b 08 00  |.*..>+...+...+..|
000002e0  8c 2c 08 00 a5 2f 08 00  ed 2f 08 00 1a 31 08 00  |.,.../.../...1..|
000002f0  6f 31 08 00 c4 31 08 00  31 32 08 00 88 32 08 00  |o1...1..12...2..|
00000300  fa 32 08 00 87 33 08 00  2c 34 08 00 75 34 08 00  |.2...3..,4..u4..|
00000310  da 34 08 00 85 35 08 00  ed 35 08 00 50 36 08 00  |.4...5...5..P6..|
00000320  a1 36 08 00 fe 36 08 00  48 37 08 00 8e 37 08 00  |.6...6..H7...7..|
00000330  ce 37 08 00 6f 38 08 00  dc 38 08 00 23 39 08 00  |.7..o8...8..#9..|
00000340  64 39 08 00 a1 39 08 00  f4 39 08 00 47 3a 08 00  |d9...9...9..G:..|
00000350  b1 3a 08 00 0f 3b 08 00  ae 3b 08 00 5a 3c 08 00  |.:...;...;..Z<..|
00000360  e4 3c 08 00 c0 3d 08 00  4a 3e 08 00 ae 3e 08 00  |.<...=..J>...>..|
00000370  0f 3f 08 00 b0 3f 08 00  83 40 08 00 f4 40 08 00  |.?...?...@...@..|
00000380  3f 41 08 00 8b 41 08 00  06 42 08 00 6d 42 08 00  |?A...A...B..mB..|
00000390  14 43 08 00 a7 43 08 00  0e 44 08 00 75 44 08 00  |.C...C...D..uD..|
000003a0  41 45 08 00 9b 45 08 00  f5 45 08 00 61 46 08 00  |AE...E...E..aF..|
000003b0  14 47 08 00 c7 47 08 00  25 48 08 00 78 48 08 00  |.G...G..%H..xH..|
000003c0  11 49 08 00 d7 49 08 00  44 4a 08 00 b3 4a 08 00  |.I...I..DJ...J..|
000003d0  20 4b 08 00 b1 4b 08 00  d7 4d 08 00 58 4e 08 00  | K...K...M..XN..|
000003e0  f5 4e 08 00 99 4f 08 00  36 50 08 00 cd 50 08 00  |.N...O..6P...P..|
000003f0  44 51 08 00 c4 51 08 00  3d 52 08 00 af 52 08 00  |DQ...Q..=R...R..|
00000400  2c 53 08 00 aa 53 08 00  15 54 08 00 af 54 08 00  |,S...S...T...T..|
00000410  4a 55 08 00 68 56 08 00  7b 57 08 00 5a 58 08 00  |JU..hV..{W..ZX..|
00000420  19 59 08 00 1c 5a 08 00  13 5b 08 00 91 5b 08 00  |.Y...Z...[...[..|
00000430  f8 5b 08 00 7c 5c 08 00  5d 5d 08 00 2a 5e 08 00  |.[..|\..]]..*^..|
00000440  8d 5e 08 00 41 5f 08 00  ef 5f 08 00 ab 60 08 00  |.^..A_..._...`..|
00000450  2f 61 08 00 ba 61 08 00  54 62 08 00 4a 63 08 00  |/a...a..Tb..Jc..|
00000460  2c 64 08 00 8d 64 08 00  58 65 08 00 dc 65 08 00  |,d...d..Xe...e..|
00000470  5c 66 08 00 dc 66 08 00  43 67 08 00 eb 67 08 00  |\f...f..Cg...g..|
00000480  71 68 08 00 f5 68 08 00  9a 69 08 00 5a 6a 08 00  |qh...h...i..Zj..|
00000490  1a 6b 08 00 e5 6c 08 00  3d 76 08 00 42 77 08 00  |.k...l..=v..Bw..|
000004a0  c5 77 08 00 8e 78 08 00  39 79 08 00 15 7a 08 00  |.w...x..9y...z..|
000004b0  f4 7a 08 00 06 7c 08 00  95 7c 08 00 63 7d 08 00  |.z...|...|..c}..|
000004c0  03 7e 08 00 b9 7e 08 00  73 7f 08 00 4b 80 08 00  |.~...~..s...K...|
000004d0  cf 80 08 00 8a 81 08 00  13 82 08 00 a0 82 08 00  |................|
000004e0  6f 83 08 00 1b 84 08 00  ce 84 08 00 9d 85 08 00  |o...............|
000004f0  2a 86 08 00 ee 86 08 00  7f 88 08 00 84 89 08 00  |*...............|
00000500  54 8a 08 00 22 8b 08 00  34 8c 08 00 47 8d 08 00  |T..."...4...G...|
00000510  f7 8d 08 00 7d 8e 08 00  03 8f 08 00 89 8f 08 00  |....}...........|
00000520  0f 90 08 00 95 90 08 00  1b 91 08 00 a0 91 08 00  |................|
00000530  25 92 08 00 aa 92 08 00  2f 93 08 00 b4 93 08 00  |%......./.......|
00000540  39 94 08 00 be 94 08 00  4d 95 08 00 52 96 08 00  |9.......M...R...|
00000550  02 97 08 00 16 98 08 00  b1 99 08 00 35 9a 08 00  |............5...|
00000560  ca 9a 08 00 56 9b 08 00  c2 9c 08 00 dc 9d 08 00  |....V...........|
00000570  8b 9e 08 00 95 9f 08 00  4e a1 08 00 7d a2 08 00  |........N...}...|
00000580  66 a3 08 00 61 a4 08 00  fe a4 08 00 93 a5 08 00  |f...a...........|
00000590  2e a6 08 00 f3 a6 08 00  bb a7 08 00 83 a8 08 00  |................|
000005a0  4b a9 08 00 13 aa 08 00  db aa 08 00 a3 ab 08 00  |K...............|
000005b0  8a ac 08 00 6a ad 08 00  3e ae 08 00 f6 ae 08 00  |....j...>.......|
000005c0  b0 b0 08 00 ab b1 08 00  94 b2 08 00 bb b3 08 00  |................|
000005d0  2a b5 08 00 bf b5 08 00  40 b6 08 00 2f b7 08 00  |*.......@.../...|
000005e0  47 b8 08 00 5f b9 08 00  3c ba 08 00 b7 bb 08 00  |G..._...<.......|
000005f0  a9 bc 08 00 d3 bd 08 00  eb be 08 00 f0 bf 08 00  |................|
00000600  b9 c0 08 00 59 c1 08 00  4b c2 08 00 08 c3 08 00  |....Y...K.......|
00000610  d5 c3 08 00 d6 c4 08 00  36 c6 08 00 86 c7 08 00  |........6.......|
00000620  66 c8 08 00 65 c9 08 00  e3 ca 08 00 d9 cb 08 00  |f...e...........|
00000630  b0 cc 08 00 d0 cd 08 00  6b cf 08 00 4f d0 08 00  |........k...O...|
00000640  33 d1 08 00 0e d2 08 00  2c d5 08 00 72 da 08 00  |3.......,...r...|
00000650  69 db 08 00 88 dd 08 00  99 df 08 00 83 e0 08 00  |i...............|
00000660  3c e3 08 00 c7 e4 08 00  5f e6 08 00 2b e7 08 00  |<......._...+...|
00000670  e6 e8 08 00 9a ea 08 00  40 eb 08 00 e3 ed 08 00  |........@.......|
00000680  59 f5 08 00 85 f6 08 00  bd f7 08 00 ac f8 08 00  |Y...............|
00000690  86 fa 08 00 f4 fb 08 00  83 fd 08 00 9e ff 08 00  |................|
000006a0  0d 01 09 00 e8 01 09 00  9e 03 09 00 50 06 09 00  |............P...|
000006b0  ff 08 09 00 a3 0b 09 00  40 12 09 00 66 14 09 00  |........@...f...|
000006c0  89 15 09 00 1e 17 09 00  76 19 09 00 1f 1b 09 00  |........v.......|
000006d0  ab 1c 09 00 99 1e 09 00  32 20 09 00 77 22 09 00  |........2 ..w"..|
000006e0  e7 25 09 00 a2 27 09 00  46 2a 09 00 8f 36 09 00  |.%...'..F*...6..|
000006f0  d2 38 09 00 65 40 09 00  20 43 09 00 35 46 09 00  |.8..e@.. C..5F..|
00000700  5f 4d 09 00 5f 5b 09 00  46 61 09 00 00 68 09 00  |_M.._[..Fa...h..|
00000710  06 6b 09 00 d6 7e 09 00  e5 9f 09 00 e7 9f 0a 00  |.k...~..........|
00000720  72 b7 0a 00 b5 e4 0a 00  7b eb 0a 00 7e eb 0a 00  |r.......{...~...|
00000730  81 eb 0a 00 85 eb 0a 00  8b eb 0a 00 b2 eb 0a 00  |................|
00000740  b8 eb 0a 00 bd eb 0a 00  cb eb 0a 00 d2 eb 0a 00  |................|
00000750  de eb 0a 00 f9 eb 0a 00  0e ec 0a 00 18 ec 0a 00  |................|
00000760  33 ec 0a 00 4b ec 0a 00  60 ec 0a 00 79 ec 0a 00  |3...K...`...y...|
00000770  8b ec 0a 00 d2 ec 0a 00  1d ed 0a 00 2c ed 0a 00  |............,...|
00000780  49 ed 0a 00 5e ed 0a 00  71 ed 0a 00 95 ed 0a 00  |I...^...q.......|
00000790  9d ed 0a 00 b7 ed 0a 00  c3 ed 0a 00 da ed 0a 00  |................|
000007a0  f0 ed 0a 00 f9 ed 0a 00  02 ee 0a 00 0a ee 0a 00  |................|
000007b0  22 ee 0a 00 2f ee 0a 00  3c ee 0a 00 49 ee 0a 00  |".../...<...I...|
000007c0  5e ee 0a 00 6b ee 0a 00  7c ee 0a 00 86 ee 0a 00  |^...k...|.......|
000007d0  91 ee 0a 00 a1 ee 0a 00  ae ee 0a 00 b7 ee 0a 00  |................|
000007e0  bf ee 0a 00 ce ee 0a 00  d6 ee 0a 00 e1 ee 0a 00  |................|
000007f0  e7 ee 0a 00 f5 ee 0a 00  fd ee 0a 00 0c ef 0a 00  |................|
00000800  17 ef 0a 00 21 ef 0a 00  29 ef 0a 00 3a ef 0a 00  |....!...)...:...|
00000810  53 ef 0a 00 5f ef 0a 00  6a ef 0a 00 7d ef 0a 00  |S..._...j...}...|
00000820  87 ef 0a 00 90 ef 0a 00  9e ef 0a 00 b4 ef 0a 00  |................|
00000830  bb ef 0a 00 c6 ef 0a 00  d2 ef 0a 00 d9 ef 0a 00  |................|
00000840  e8 ef 0a 00 f5 ef 0a 00  fc ef 0a 00 0b f0 0a 00  |................|
00000850  17 f0 0a 00 27 f0 0a 00  2a f0 0a 00 2e f0 0a 00  |....'...*.......|
00000860  32 f0 0a 00 37 f0 0a 00  3c f0 0a 00 41 f0 0a 00  |2...7...<...A...|
00000870  46 f0 0a 00 4b f0 0a 00  67 f0 0a 00 79 f0 0a 00  |F...K...g...y...|
00000880  90 f0 0a 00 a4 f0 0a 00  bd f0 0a 00 d2 f0 0a 00  |................|
00000890  ec f0 0a 00 02 f1 0a 00  17 f1 0a 00 2e f1 0a 00  |................|
000008a0  42 f1 0a 00 58 f1 0a 00  6d f1 0a 00 83 f1 0a 00  |B...X...m.......|
000008b0  9c f1 0a 00 ba f1 0a 00  d4 f1 0a 00 ee f1 0a 00  |................|

dexdump

~/.android-sdk/build-tools/23.0.1/dexdump -d \
      ./target/android/dex/classes.dex \
      > ./target/android/dex/classes.dex.dump
Processing './target/android/dex/classes.dex'...
Opened './target/android/dex/classes.dex', DEX version '035'
Class #0            -
  Class descriptor  : 'L_00_helloworld/HelloWorldKt;'
  Access flags      : 0x0011 (PUBLIC FINAL)
  Superclass        : 'Ljava/lang/Object;'
  Interfaces        -
  Static fields     -
  Instance fields   -
  Direct methods    -
    #0              : (in L_00_helloworld/HelloWorldKt;)
      name          : 'main'
      type          : '([Ljava/lang/String;)V'
      access        : 0x0019 (PUBLIC STATIC FINAL)
      code          -
      registers     : 2
      ins           : 1
      outs          : 2
      insns size    : 13 16-bit code units
020494:                                        |[020494] _00_helloworld.HelloWorldKt.main:([Ljava/lang/String;)V
0204a4: 1a00 1310                              |0000: const-string v0, "args" // string@1013
0204a8: 7120 a811 0100                         |0002: invoke-static {v1, v0}, Lkotlin/jvm/internal/Intrinsics;.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V // method@11a8
0204ae: 1a01 0808                              |0005: const-string v1, "Hello RivieraDev" // string@0808
0204b2: 6200 0c00                              |0007: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@000c
0204b6: 6e20 3f00 1000                         |0009: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@003f
0204bc: 0e00                                   |000c: return-void
      catches       : (none)
      positions     :
        0x0007 line=4
      locals        :

  Virtual methods   -
  source_file_idx   : 2057 (HelloWorld.kt)
...

smali

sh ./scripts/lib/dextools/d2j-dex2smali.sh \
     ./target/android/dex/classes.dex -f \
     -o ./target/android/smali
.class public final L_00_helloworld/HelloWorldKt;
.super Ljava/lang/Object;
.source "HelloWorld.kt"

.annotation system Ldalvik/annotation/SourceDebugExtension;
  value = "SMAP\nHelloWorld.kt\nKotlin\n*S Kotlin\n*F\n+ 1 HelloWorld.kt\n_00_helloworld/HelloWorldKt\n*L\n1#1,5:1\n*E\n"
.end annotation
.annotation runtime Lkotlin/Metadata;
  bv = {
    1,
    0,
    2
  }
  d1 = {
    "\u0000\u0012\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0000\u001a\u0019\u0010\u0000\u001a\u00020\u00012\u000c\u0010\u0002\u001a\u0008\u0012\u0004\u0012\u00020\u00040\u0003\u00a2\u0006\u0002\u0010\u0005"
  }
  d2 = {
    "main",
    "",
    "args",
    "",
    "",
    "([Ljava/lang/String;)V"
  }
  k = 2
  mv = {
    1,
    1,
    9
  }
.end annotation

.method public final static main([Ljava/lang/String;)V
  .parameter # [Ljava/lang/String;
    .annotation build Lorg/jetbrains/annotations/NotNull;
    .end annotation
  .end parameter
  .registers 2
    const-string v0, "args"
    invoke-static { p0, v0 }, Lkotlin/jvm/internal/Intrinsics;->checkParameterIsNotNull(Ljava/lang/Object;Ljava/lang/String;)V
    const-string p0, "Hello RivieraDev"
  .line 4
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
    invoke-virtual { v0, p0 }, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
    return-void
.end method

Autres structures

Autres structures

if-then-else.kt

fun handleAstronomicalBody(body: AstronomicalBody) {
    val message =
        if (body is Planet && body.name == "Earth") {
            "Welcome Earth"
        } else {
            "Welcome martian"
        }
    println(message)
}

for.kt

fun main(args: Array<String>) {
    for (body in SolarSystem.bodies) { // 🤢
        print(body)
    }
}

Mais aussi des while et do while

et des break, continue, et label

when.kt

for (body in SolarSystem.bodies) { // 🤢

    val message = when (body) {
        is Planet -> "Planet ${body.name}"
        is Star   -> "Star ${body.name}"
        else      -> null
    }

    if (message != null) {
        println(message)
    }
}

for-factorial.kt

// Note: assert(n >= 0)
fun forFactorial(n: Int): Int { // 🤢
    var acc = 1
    for (i in 1..n) {
        acc *= i
    }
    return acc
}

ByteCode factoriel avec for

Compiled from "for-factorial.kt"
public final class For_factorialKt {
  public static final int forFactorial(int);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_1
       3: istore_2
       4: iload_0
       5: istore_3
       6: iload_2
       7: iload_3
       8: if_icmpgt     26
      11: iload_1
      12: iload_2
      13: imul
      14: istore_1
      15: iload_2
      16: iload_3
      17: if_icmpeq     26
      20: iinc          2, 1
      23: goto          11
      26: iload_1
      27: ireturn
}

rec-factorial.kt

// Note: assert(n >= 0)
fun recFactorial(n: Int): Int =
    if (n < 1) 1
    else n * recFactorial(n - 1)

ByteCode factoriel avec récursivité

Compiled from "rec-factorial.kt"
public final class Rec_factorialKt {
  public static final int recFactorial(int);
    Code:
       0: iload_0
       1: iconst_1
       2: if_icmpge     9
       5: iconst_1
       6: goto          17
       9: iload_0
      10: iload_0
      11: iconst_1
      12: isub
      13: invokestatic  #8                  // Method recFactorial:(I)I
      16: imul
      17: ireturn
}

Récursion non terminale

`x! = x xx (x-1) xx ... xx 2 xx 1` `1! = 0! = 1`
  • `fact(x)`
  • `x xx fact(x-1)`
    • `x`
  • `x xx (x-1) xx fact(x-2)`
    • `x - 1`
    • `x`
  • `x xx (x-1) xx (x-2) xx ...`
    • ...
    • `x - 1`
    • `x`
  • `x xx (x-1) xx (x-2) xx ... xx 2 xx 1`
    • `1`
    • `2`
    • ...
    • `x - 1`
    • `x`

Récursion terminale

  • `fact(x) = fact(x, 1)`
  • `fact(x-1, x xx 1)`
  • `fact(x-2, x xx (x-1))`
  • `fact(... , x xx (x-1) xx (x-2) xx ...)`
  • `fact(1, x xx (x-1) xx (x-2) xx ... xx 2)`
  • ⚠️ Nécessite une optimisation par le compilateur

tailrec-factorial.kt

// Note: assert(n >= 0)
fun tailRecFactorial(n: Int): Int {

    tailrec fun aux(n: Int, acc: Int): Int =
        if (n < 1) acc
        else aux(n - 1, acc * n)

    return aux(n, 1)
}

ByteCode factoriel avec recursivité terminale 1/2

public final class Tailrec_factorialKt {
  public static final int tailRecFactorial(int);
    Code:
       0: getstatic     #12                 // Field Tailrec_factorialKt$tailRecFactorial$1.INSTANCE:LTailrec_factorialKt$tailRecFactorial$1;
       3: astore_1
       4: aload_1
       5: iload_0
       6: iconst_1
       7: invokevirtual #16                 // Method Tailrec_factorialKt$tailRecFactorial$1.invoke:(II)I
      10: ireturn
}

ByteCode factoriel avec recursivité terminale 2/2

Compiled from "tailrec-factorial.kt"
final class Tailrec_factorialKt$tailRecFactorial$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function2<java.lang.Integer, java.lang.Integer, java.lang.Integer> {
  public static final Tailrec_factorialKt$tailRecFactorial$1 INSTANCE;

  public java.lang.Object invoke(java.lang.Object, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #11                 // class java/lang/Number
       5: invokevirtual #15                 // Method java/lang/Number.intValue:()I
       8: aload_2
       9: checkcast     #11                 // class java/lang/Number
      12: invokevirtual #15                 // Method java/lang/Number.intValue:()I
      15: invokevirtual #18                 // Method invoke:(II)I
      18: invokestatic  #24                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      21: areturn

  public final int invoke(int, int);
    Code:
       0: iload_1
       1: iconst_1
       2: if_icmpge     9
       5: iload_2
       6: goto          25
       9: aload_0
      10: checkcast     #2                  // class Tailrec_factorialKt$tailRecFactorial$1
      13: pop
      14: iload_1
      15: iconst_1
      16: isub
      17: iload_2
      18: iload_1
      19: imul
      20: istore_2
      21: istore_1
      22: goto          0
      25: ireturn

  Tailrec_factorialKt$tailRecFactorial$1();
    Code:
       0: aload_0
       1: iconst_2
       2: invokespecial #34                 // Method kotlin/jvm/internal/Lambda."<init>":(I)V
       5: return

  static {};
    Code:
       0: new           #2                  // class Tailrec_factorialKt$tailRecFactorial$1
       3: dup
       4: invokespecial #56                 // Method "<init>":()V
       7: putstatic     #58                 // Field INSTANCE:LTailrec_factorialKt$tailRecFactorial$1;
      10: return
}

Performances sur 10!

433372259 ops/s
433372259 ops/s
374900724 ops/s
374900724 ops/s
71945600 ops/s
71945600 ops/s
75889169 ops/s
75889169 ops/s
74708349 ops/s
74708349 ops/s
432005904 ops/s
432005904 ops/s
21560856 ops/s
21560856 ops/s
99169023 ops/s
99169023 ops/s
Benchmark Factoriel

Bilan structures

  • when peut être utilisé avec

    • des constantes

    • plusieurs valeurs séparées par ,

    • une expression

    • avec is et un type (avec un 'smart cast')

  • privilégier les when si vous avez plus de 2 cas

    si vous faites des fonctions récursives, faites les tailrec

Extensions de fonctions

Extensions de fonctions

extension.kt

val AstronomicalBody.size: Int
    get() = name.length

fun AstronomicalBody.display() = "Body $name $size"

fun main(args: Array<String>) {
    SolarSystem.bodies
        .forEach { println(it.display()) }
}

extension.java

import java.util.Iterator;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000 \n\u0000\n\u0002\u0010\b\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0003\u001a\u0019\u0010\u0005\u001a\u00020\u00062\f\u0010\u0007\u001a\b\u0012\u0004\u0012\u00020\t0\b¢\u0006\u0002\u0010\n\u001a\n\u0010\u000b\u001a\u00020\t*\u00020\u0002\"\u0015\u0010\u0000\u001a\u00020\u0001*\u00020\u00028F¢\u0006\u0006\u001a\u0004\b\u0003\u0010\u0004¨\u0006\f"},
        d2 = {"size", "", "LAstronomicalBody;", "getSize", "(LAstronomicalBody;)I", "main", "", "args", "", "", "([Ljava/lang/String;)V", "display", "kotlinDee"}
)
public final class ExtensionKt {
   public static final int getSize(@NotNull AstronomicalBody $this$size) {
      Intrinsics.checkParameterIsNotNull($this$size, "$this$size");
      return $this$size.getName().length();
   }

   @NotNull
   public static final String display(@NotNull AstronomicalBody $this$display) {
      Intrinsics.checkParameterIsNotNull($this$display, "$this$display");
      return "Body " + $this$display.getName() + ' ' + getSize($this$display);
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      Iterable $this$forEach$iv = (Iterable)SolarSystem.INSTANCE.getBodies();
      int $i$f$forEach = false;
      Iterator var3 = $this$forEach$iv.iterator();

      while(var3.hasNext()) {
         Object element$iv = var3.next();
         AstronomicalBody it = (AstronomicalBody)element$iv;
         int var6 = false;
         String var7 = display(it);
         boolean var8 = false;
         System.out.println(var7);
      }

   }
}

Extension

Les collections

Les collections

Collections

Les collections

Les Maps

Les Maps

api.kt

val s = SolarSystem.bodies
    .filterIsInstance<Planet>()
    .flatMap { planet -> planet.moons } // 😻
    .filterNot { it.name.startsWith("S/") }
    .sortedBy { it.name }
//        .fold("") { acc, moon ->
//            (if (acc == "") "" else "$acc,\n") + moon.name
//        }
    .joinToString(",\n") { it.name }

println(s)

break-immutable.kt

fun main(args: Array<String>) {
    val moons = (1..9).map { Moon("Moon #$it") }.toList()

    println(moons.javaClass) // class java.util.ArrayList

    moons.javaClass.methods
        .find { it.name == "add" && it.parameterCount == 1 }
        ?.invoke(moons, Moon("XXX"))

    println(moons.joinToString("\n"))
    // Moon(name=Moon #1)
    // Moon(name=Moon #2)
    // Moon(name=Moon #3)
    // Moon(name=Moon #4)
    // Moon(name=Moon #5)
    // Moon(name=Moon #6)
    // Moon(name=Moon #7)
    // Moon(name=Moon #8)
    // Moon(name=Moon #9)
    // Moon(name=XXX)
}

sequence.kt

val s = SolarSystem.bodies.asSequence()
    .filterIsInstance<Planet>()
    .flatMap { planet -> planet.moons.asSequence() } // 😻
    .filterNot { it.name.startsWith("S/") }
    .sortedBy { it.name }
    .joinToString(",\n") { it.name }

println(s)

Performance des séquences 1/2

Benchmark Mode Cnt Score Error Units
ApiClassic thrpt 200 44535.029 3550.944 ops/s
ApiSequence thrpt 200 23652.238 1967.535 ops/s
JMH benchmarks
44535 ops/s
44535 ops/s
23652 ops/s
23652 ops/s
Benchmark séquences 1

sequence2.kt

val s = SolarSystem.bodies.asSequence()
    .filterIsInstance<Planet>()
    .flatMap { planet -> planet.moons.asSequence() } // 😻
    .filterNot { it.name.startsWith("S/") }
    .map { it.name }
    .first()

println(s)

Performance des séquences 2/2

Benchmark Mode Cnt Score Error Units
ApiClassicFirst thrpt 200 241752.062 5022.663 ops/s
ApiSequenceFirst thrpt 200 3615451.391 454502.198 ops/s
JMH benchmarks
241752 ops/s
241752 ops/s
3615451 ops/s
3615451 ops/s
Benchmark sequences 1

Bilan collection

  • 💪 Super on a de l'immutabilité, des map, flatMap, fold, aggregate,...

  • 🤨 Mais ça reste des collections Java

  • API standard avec Range, Pair, et Triple

  • 📏 Avant d'utiliser les Sequence, faites des mesures

Les delegates

Les delegates

delegate.kt

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

fun main(args: Array<String>) {
    val value: String by MyDelegateClass()
    println(value)
}

class MyDelegateClass : ReadOnlyProperty<Nothing?, String> {
    override operator fun getValue(thisRef: Nothing?,
                                   property: KProperty<*>) = "Hello RivieraDev"
}

lazy.kt

object DeepThought {
    fun answer(): Int {
        print("Computing ...")
        return 42
    }
}

fun main() {

    val ultimateQuestionOfLife: Int by lazy {
        DeepThought.answer()
    }
    println("The Ultimate Question of Life, " +
                    "the Universe and Everything ?")
    println("Answer: $ultimateQuestionOfLife" )
}

observable.kt

import kotlin.properties.Delegates

fun main(args: Array<String>) {

    var observable: String by Delegates.observable("Initial value") {
        _, old, new ->
            println("$old -> $new")
    }

    observable = "new value"
}

lateinit.kt

object Plop {
    lateinit var str: String

    fun info() {
        println(Plop::str.isInitialized)
    }
}

fun main() {
    Plop.info() // false
    Plop.str = "Hello"
    Plop.info() // true

    println(str) // Hello
}

Delegate

  • Lazy : utile pour les propriétés qui ne sont pas systématiquement utilisées.

  • À manipuler avec précaution dans les activités Android ( avec le cycle de vie, cela peut référencer une ancienne instance)

  • Delegate : Observable, Not null, ...

  • lateinit : évite les null check pour les propriétés qui ne peuvent être initialisées immédiatement (ex: référence de vues sur Activity, Fragment).

  • Ne peut pas être utilisé avec les types primitifs

inline

inline

inline.kt

object Logger {
    var enable: Boolean = false

    inline fun log(msg: () -> String) {
        if (enable) {
            println(msg())
        }
    }
}

fun main() {
    Logger.log { "Hello" }
}

Logger.java

Compiled from "inline-fun.kt"
public final class Inline_funKt {
  public static final void main();
    Code:
       0: getstatic     #15                 // Field Logger.INSTANCE:LLogger;
       3: astore_0
       4: iconst_0
       5: istore_1
       6: aload_0
       7: invokevirtual #19                 // Method Logger.getEnable:()Z
      10: ifeq          27
      13: iconst_0
      14: istore_2
      15: ldc           #21                 // String Hello
      17: astore_2
      18: iconst_0
      19: istore_3
      20: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      23: aload_2
      24: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      27: nop
      28: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #9                  // Method main:()V
       3: return
}

reified.kt

class Pojo {
    var name: String? = null
    override fun toString() = "Pojo $name"
}

object JavaBeanBuilder {

    fun <T> createBean(clazz: Class<T>): T =
        clazz.newInstance()

    inline fun <reified T> createBean(): T =
        createBean(T::class.java)
}

fun main(args: Array<String>) {
    val p1 = Pojo()
    p1.name = "Plop1"
    println(p1)

    val p2 = JavaBeanBuilder.createBean<Pojo>()
    p2.name = "Plop2"
    println(p2)
}

reified.java

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"},
        d2 = {"main", "", "args", "", "", "([Ljava/lang/String;)V", "kotlinDee"}
)
public final class ReifiedKt {
    public static final void main(@NotNull String[] args) {
        Intrinsics.checkParameterIsNotNull(args, "args");
        Pojo p1 = new Pojo();
        p1.setName("Plop1");
        boolean var2 = false;
        System.out.println(p1);
        JavaBeanBuilder this_$iv = JavaBeanBuilder.INSTANCE;
        int $i$f$createBean = false;
        Pojo p2 = (Pojo)this_$iv.createBean(Pojo.class);
        p2.setName("Plop2");
        boolean var6 = false;
        System.out.println(p2);
    }
}

geoloc.kt

inline class Latitude(val value: Double)
inline class Longitude(val value: Double)

data class Geoloc(val lat: Latitude, val lng: Longitude) {

    infix fun distanceTo(other: Geoloc): Double = TODO()
}

fun main() {
    val toulouse = Geoloc(Latitude(43.60426), Longitude(1.44367))
    val nice = Geoloc(Latitude(43.70313), Longitude(7.26608))

    val travel = toulouse distanceTo nice
    println(travel)
}

geoloc bytecode

Compiled from "geoloc.kt"
public final class Geoloc {
  public final double distanceTo(Geoloc);
    Code:
       0: aload_1
       1: ldc           #9                  // String other
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_0
       7: istore_2
       8: new           #17                 // class kotlin/NotImplementedError
      11: dup
      12: aconst_null
      13: iconst_1
      14: aconst_null
      15: invokespecial #21                 // Method kotlin/NotImplementedError."<init>":(Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
      18: checkcast     #23                 // class java/lang/Throwable
      21: athrow

  public final double getLat();
    Code:
       0: aload_0
       1: getfield      #31                 // Field lat:D
       4: dreturn

  public final double getLng();
    Code:
       0: aload_0
       1: getfield      #35                 // Field lng:D
       4: dreturn

  public Geoloc(double, double, kotlin.jvm.internal.DefaultConstructorMarker);
    Code:
       0: aload_0
       1: dload_1
       2: dload_3
       3: invokespecial #42                 // Method "<init>":(DD)V
       6: return
}

Bilan inline

Coroutines

Coroutines

Callback hell problem

fun main(param: Int): Int {
    val step1Result = step1(param)
    return step2(step1Result)
}

Utilisons un callback

  • fun main(param: Int): Int {
        val step1Result = step1(param)
        return step2(step1Result)
    }
    
  • `=>`
  • fun main(param: Int): Int {
        return step1(param) { step1Result ->
            step2(step1Result)
        }
    

Ajoutons une étape

  • fun main(param: Int): Int {
        val step1Result = step1(param)
        val step2Result = step2(step1Result)
        return step3Result = step3(step2Result)
    }
    
  • `=>`
  • fun main(param: Int): Int {
        return step1(param) { step1Result ->
            step2(step1Result) { step2Result ->
                step3(step2Result)
            }
        }
    

suspend

suspend fun main(param: Int): Int {
    val step1Result = step1(param)
    val step2Result = step2(step1Result)
    return step3Result = step3(step2Result)
}

Continuation

suspend fun step2(step1Result: Int): Int = TODO()
import kotlin.Metadata;
import kotlin.NotImplementedError;
import kotlin.coroutines.Continuation;
import kotlin.jvm.internal.DefaultConstructorMarker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
        mv = {1, 1, 15},
        bv = {1, 0, 3},
        k = 2,
        d1 = {"\u0000\n\n\u0000\n\u0002\u0010\b\n\u0002\b\u0003\u001a\u0019\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0003\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u0004"},
        d2 = {"step2", "", "step1Result", "(ILkotlin/coroutines/Continuation;)Ljava/lang/Object;", "devoxxfr"}
)
public final class BaseKt {
    @Nullable
    public static final Object step2(int step1Result, @NotNull Continuation $completion) {
        boolean var2 = false;
        throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
    }
}
API Continuation

Plus sur les coroutines

Conclusion

Conclusion

Android

Serveur

Web et Natif

Bilan

  • 💎 JVM

  • 😎 Le byte code c'est cool

  • 🔮 Généralement, ça ne suffit pas pour prédire les performances

  • 📏 Mesurez !

Kotlin vs Java

  • C'est déjà mature

  • ✊ Code plus expressif, plus sûr, plus simple

  • 🤝 Interopérable avec Java

  • 👍 Outillage (éditeur, gradle, maven)

  • 👍 Ecosystème et communauté

  • 🚀 Évolution rapide

  • 🐣 Code multiplatform

  • Kotlin réussit une belle alchimie entre pragmatisme, puissance, sûreté, accessibilité.

Merci

Questions ?