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

JUGNsk Meetup #3. Илья Матвеев: "Kotlin вообще и Native в частности"

jugnsk
August 23, 2018

JUGNsk Meetup #3. Илья Матвеев: "Kotlin вообще и Native в частности"

Наверняка вы слышали про Kotlin: новый JVM-based язык от JetBrains. Но знаете ли вы, что он также компилируется в JavaScript или в нативный код? В докладе расскажем о проекте Kotlin/Native, что это такое, и чем он отличается от Kotlin/JVM. Также поговорим о поддержке мультиплатформенных проектов на Kotlin, и сложностях и возможностях, которые привносит компиляции Kotlin напрямую в машинный код.

jugnsk

August 23, 2018
Tweet

More Decks by jugnsk

Other Decks in Programming

Transcript

  1. О чём будем говорить? • Kotlin - история и принципы

    • Фичи языка • Мультиплатформенные проекты • Kotlin/Native 2
  2. Немного истории В начале было слово. И слово это было

    “Java” • 2010 - Начало разработки Котлина • 2012 - Открыты исходники (Apache 2.0) • 2016 - Релиз 1.0 • 2017 - Котлин - официальный язык для Android • 2017 - Релиз 1.2 4
  3. Что такое Котлин? • Статически типизированный язык для современных мультиплатформенных

    приложений (kotlinlang.org) • Компилируемся в: ◦ JVM-байткод ◦ JavaScript ◦ Машинный код 6
  4. Система типов • Сильная статическая типизация • Автовывод типов •

    Нет разделения на примитивные и ссылочные типы 10
  5. Система типов. Примитивы var intVar: Int = 42 var anyVar:

    Any = intVar var str: String = intVar.toString() 11 int intVar = 42; Object anyVar = Integer. valueOf(intVar); String str = String.valueOf(intVar);
  6. Система типов • Сильная статическая типизация • Автовывод типов •

    Нет разделения на примитивные и ссылочные типы • Smart casts 12
  7. Система типов. Smart casts fun getLength(obj: Any): Int { if

    (obj is String) { return obj.length } return -1 } 13 Cast Any → String
  8. Система типов • Сильная статическая типизация • Автовывод типов •

    Нет разделения на примитивные и ссылочные типы • Smart casts • Nullability 14
  9. Nullability var string: String = "non-nullable string" var nullableString: String?

    = "nullable string" nullableString = null <- ok string = null <- Compilation error! 16
  10. Nullability var string: String = "non-nullable string" var nullableString: String?

    = "nullable string" nullableString = null <- ok string = null <- Compilation error! nullableString = string <- ok string = nullableString <- Compilation error! 17
  11. Nullability var string: String = "non-nullable string" var nullableString: String?

    = "nullable string" nullableString = null <- ok string = null <- Compilation error! nullableString = string <- ok string = nullableString <- Compilation error! string.length <- ok nullableString.length <- Compilation error! 18
  12. Nullability. Smart casts if (nullableString != null) { nullableString.length <-

    ok. Cast String? -> String } nullableString.length <- Compilation error! 19
  13. Safe calls & Elvis operator val nullableLength: Int? = nullableString?.length

    20 Safe call. Обращение к length случится только если строка не null
  14. Safe calls & Elvis operator val nullableLength: Int? = nullableString?.length

    val string: String = nullableString ?: "Default" 21 Elvis. Получим “Default”, если строка равна null
  15. Safe calls & Elvis operator val nullableLength: Int? = nullableString?.length

    val string: String = nullableString ?: "Default" val length: Int = nullableString?.length ?: -1 22 Операторы можно комбинировать
  16. Nullability. Интероп с Java public class Foo { static String

    foo() { /* ... */ } } 24 val string = Foo.foo() String? или String
  17. Nullability. Интероп с Java public class Foo { static String

    foo() { /* ... */ } } 25 val string = Foo.foo() : String!
  18. Nullability. Интероп с Java public class Foo { @NotNull static

    String foo() { /* ... */ } } 26 val string = Foo.foo() : String Понимаем аннотации
  19. Компактно и читаемо. Классы... public class User { private final

    String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 27
  20. Компактно и читаемо. Классы... public class User { private final

    String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class User { val name: String var age: Int constructor(name: String, age: Int) { this.name = name this.age = age } } 28
  21. Компактно и читаемо. Классы... public class User { private final

    String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class User { val name: String var age: Int constructor(name: String, age: Int) { this.name = name this.age = age } } 29 val user = User("John", 20) val foo = user.name user.age = 42
  22. Компактно и читаемо. Классы... public class User { private final

    String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class User { val name: String var age: Int constructor(name: String, age: Int) { this.name = name this.age = age } } 30
  23. Компактно и читаемо. Классы... public class User { private final

    String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class User(val name: String, var age: Int) {} 31
  24. … и прочее 32 • Data-классы • Оператор when -

    switch на стероидах • Паттерн “Делегат” в одну строчку кода • Перегрузка операторов • и т.д.
  25. Extension-функции fun <T> getOrNull(list: List<T>, index: Int): T? { if

    (index >= 0 && index < list.size) { return list.get(index) } else { return null } } val foo = getOrNull(list, index) 33
  26. Extension-функции fun <T> List<T>.getOrNull( index: Int): T? { if (index

    >= 0 && index < size) { return get(index) } else { return null } } val foo = list.getOrNull( index) 34
  27. Extension-функции fun <T> List<T>.getOrNull(index: Int): T? { if (index >=

    0 && index < size) { return get(index) } else { return null } } val foo = list.getOrNull(index) 35 Список доступен внутри extension-а как this
  28. Лямбды и inline-функции fun IntArray.forEach(action: (Int) -> Unit) { for

    (i in 0 until size) { val element = get(i) action(element) } } 36
  29. Лямбды и inline-функции fun IntArray.forEach(action: (Int) -> Unit) { for

    (i in 0 until size) { val element = get(i) action(element) } } array.forEach({ element -> println(element) }) 37
  30. Лямбды и inline-функции fun IntArray.forEach(action: (Int) -> Unit) { for

    (i in 0 until size) { val element = get(i) action(element) } } array.forEach { element -> println(element) } 38
  31. Лямбды и inline-функции fun IntArray.forEach(action: (Int) -> Unit) { for

    (i in 0 until size) { val element = get(i) action(element) } } array.forEach { println(it) } 39
  32. Лямбды и inline-функции fun sum(array: IntArray): Int { var sum

    = 0 array.forEach { element -> sum += element } return sum } 40
  33. Лямбды и inline-функции fun sum(array: IntArray): Int { var sum

    = 0 array.forEach { element -> sum += element } return sum } 41 // ... Function1 lambda = new Function1() { public void invoke(int element) { /* ... */ } }; forEach(array, lambda); // ...
  34. Лямбды и inline-функции inline fun IntArray.forEach(action: (Int) -> Unit) {

    for (i in 0 until size) { val element = get(i) action(element) } } 42
  35. Лямбды и inline-функции fun sum(array: IntArray): Int { var sum

    = 0 array.forEach { element -> sum += element } return sum } 43
  36. Лямбды и inline-функции 44 public int sum(int[] array) { int

    sum = 0; for (int i = 0; i < array.length; i++) { int element = array[i]; sum += element; } return sum; } Тело функции forEach
  37. Лямбды и inline-функции 45 public int sum(int[] array) { int

    sum = 0; for (int i = 0; i < array.length; i++) { int element = array[i]; sum += element; } return sum; } Тело функции forEach Тело лямбды
  38. Лямбды и inline-функции val monitor = Any() // ... synchronized(monitor)

    { println("Hello from a synchronized code!") } 47 inline fun <R> synchronized(lock: Any, block: () -> R): R { /* ... */ }
  39. Extension-лямбды inline fun buildString( builderAction: StringBuilder.() -> Unit ): String

    { val builder = StringBuilder() builder.builderAction() return builder.toString() } 48
  40. Extension-лямбды inline fun buildString( builderAction: StringBuilder.() -> Unit ): String

    { val builder = StringBuilder() builder.builderAction() return builder.toString() } 49 Внутри лямбды StringBuilder будет доступен как this
  41. Extension-лямбды inline fun buildString( builderAction: StringBuilder.() -> Unit ): String

    { val builder = StringBuilder() builder.builderAction() return builder.toString() } 50 Внутри лямбды StringBuilder будет доступен как this Вызов лямбды на объекте StringBuilder
  42. Extension-лямбды val string = buildString { append("The list:") for (value

    in list) { append(" ") append(value) } } 52 Можем вызывать методы StringBuilder’а this: StringBuilder
  43. Итоги. Котлин • Котлин - язык для практического применения •

    Простые и выразительные конструкции → код короче, безопаснее и читабельнее 53
  44. При чем тут Котлин? • Умеем в Web и iOS

    • Прозрачно взаимодействуем с платформенными API 57
  45. При чем тут Котлин? • Умеем в Web и iOS

    • Прозрачно взаимодействуем с платформенными API • Упрощаем тулинг 58
  46. Мультиплатформенные проекты Android код Разделяемый код iOS код Платформенный код

    • UI • Платформенные API Разделяемый код • Высокоуровневая логика • Работа с сетью/БД 60
  47. Expect/actual expect class Logger { fun log(message: String) } import

    android.util.Log actual class Logger { fun log(message: String) { Log.i("Tag", message) } } import platform.Foundation.* actual class Logger() { fun log(message: String) { NSLog(message) } } 64
  48. Expect/actual expect class Logger { constructor(tag: String) // ... }

    expect fun withLogger(action: Logger.() -> Unit) expect fun Logger.logError(exception: Throwable) 65 Expect-класс может иметь конструктор Можно объявлять expect-функции
  49. Expect/actual package kotlin.collections expect class ArrayList<E> : MutableList<E>, RandomAccess {

    /* ... */ } package kotlin.collections actual typealias ArrayList<E> = java.util.ArrayList<E> 67
  50. Итоги. Мультиплатформа • Общий код можно разделять между платформами •

    При этом сохраняя доступ к платформенным API в платформенном коде • И не усложняя тулинг 68
  51. Kotlin/Native • Компилятор Котлина в нативный код на базе LLVM

    • Не предоставляет интероп с Kotlin/JVM • Техническое превью (текущая версия 0.8.2) • Есть на Гитхабе: github.com/JetBrains/kotlin-native/ • Платформы: ◦ Хост: Linux, macOS, Windows ◦ Кросс-компиляция: iOS, Android NDK, WebAssembly, STM32 ◦ Архитектуры: x86, ARM, MIPS, WebAssembly 70
  52. Зачем? 71 • iOS • Микроконтроллеры • WebAssembly • Время

    запуска • Размер дистрибутива • Нативные библиотеки • Глобальные оптимизации • Избавление от наследия Java
  53. Циклический мусор 74 А RC: 1 B RC: 1 Объекты

    A и B недоступны, но не могут быть уничтожены, т.к. их счетчики ссылок ≠ 0
  54. Многопоточность и модель памяти 78 val ptr = detachObjectGraph {

    Data("Hello!") } // ... val attached = attachObjectGraph<Data>(ptr) • Объекты принадлежат только одному потоку • Владение объектом можно передать
  55. “Заморозка” объектов 79 • Объект может быть заморожен • Замороженный

    объект: ◦ неизменяем ◦ может разделяться между потоками
  56. “Заморозка” объектов 80 val message = Message() message.text = "Hello!"

    message.freeze() async { use(message) } use(message) Замораживаем объект... … и можем использовать его из разных потоков
  57. Интероп Котлин → C • Нет необходимости вручную писать биндинги

    • Системные библиотеки доступны “из коробки” • Остальные подключаются специальной утилитой 81
  58. Интероп Котлин → C cinterop Def-файл 82 depends = posix

    package = platform.zlib headers = zconf.h zlib.h linkerOpts = -lz
  59. Интероп Котлин → C cinterop Def-файл 83 depends = posix

    package = platform.zlib headers = zconf.h zlib.h linkerOpts = -lz Headers
  60. Интероп Котлин → C cinterop Def-файл Interop library 84 depends

    = posix package = platform.zlib headers = zconf.h zlib.h linkerOpts = -lz Headers
  61. Интероп Котлин → C val file = fopen(fileName, "r") memScoped

    { val buffer = allocArray<ByteVar>(bufferLength) for (i in 1..count) { val nextLine = fgets(buffer, bufferLength, file) // ... } } fclose(file) 85
  62. Интероп Котлин → C val file = fopen(fileName, "r") memScoped

    { val buffer = allocArray<ByteVar>(bufferLength) for (i in 1..count) { val nextLine = fgets(buffer, bufferLength, file) // ... } } fclose(file) 86 Вызываем функции из stdio.h
  63. Интероп Котлин → C val file = fopen(fileName, "r") memScoped

    { val buffer = allocArray<ByteVar>(bufferLength) for (i in 1..count) { val nextLine = fgets(buffer, bufferLength, file) // ... } } fclose(file) 87 buffer освобождается после выхода из memScoped Аллоцируем нативную память
  64. Интероп C → Котлин • Порождаем статические и динамические библиотеки

    • Генерируем заголовочник, описывающий API • Можно использовать библиотеки на Котлине из языков, имеющих интероп с Си (например, Python) 88
  65. Интероп C → Котлин package demo class Session(val name: String,

    val number: Int) class Server(val prefix: String) { fun greet(session: Session) { return "$prefix: Hello from Kotlin/Native" } } 89
  66. Интероп C → Котлин #include "server_api.h" #define __ server_symbols()-> server_kref_demo_Server

    server = __ kotlin.root.demo.Server.Server("The server"); server_kref_demo_Session session = __ kotlin.root.demo.Session.Session("The session", 42); const char* string = __ kotlin.root.demo.Server.greet(server, session); __ DisposeString(string); __ DisposeStablePointer (server.pinned); __ DisposeStablePointer (session.pinned); 90
  67. Интероп Котлин ↔ Objective-C • Библиотеки подключаются так же как

    и сишные • Можно вызывать Котлин из Objective-C или Swift 91
  68. Интероп Котлин ↔ Objective-C • Библиотеки подключаются так же как

    и сишные • Можно вызывать Котлин из Objective-C или Swift • Более глубокая интеграция по фичам языка: ◦ Доступны классы, интерфейсы, наследование ◦ Базовые типы Котлина и Objective-C отображаются друг на друга ◦ Работает автоматическое управление памятью 92
  69. Интероп Swift → Котлин → Objective-C val kotlin10Release = NSISO8601DateFormatter()

    .dateFromString( "2016-02-15T00:00:00Z")!! fun isItTimeForKotlin(date: NSDate): String { if (date.timeIntervalSinceDate(kotlin10Release) >= 0) return "sure!" else return "yes" } 93
  70. Интероп Swift → Котлин → Objective-C val kotlin10Release = NSISO8601DateFormatter()

    .dateFromString( "2016-02-15T00:00:00Z")!! fun isItTimeForKotlin(date: NSDate): String { if (date.timeIntervalSinceDate(kotlin10Release) >= 0) return "sure!" else return "yes" } 94 Вызываем функции Objective-C
  71. Интероп Swift → Котлин → Objective-C val kotlin10Release = NSISO8601DateFormatter()

    .dateFromString( "2016-02-15T00:00:00Z")!! fun isItTimeForKotlin(date: NSDate): String { if (date.timeIntervalSinceDate(kotlin10Release) >= 0) return "sure!" else return "yes" } let now = Date() print(Common.isItTimeForKotlin (date: now)) 95 Вызываем функцию на Котлине
  72. Tooling • Плагины для CLion и AppCode ◦ Автодополнение и

    навигация ◦ Рефакторинги ◦ Запуск тестов ◦ Отладка через LLDB • Плагин для Gradle ◦ Мультиплатформенные проекты ◦ Публикация • kotlin-multiplatform (WIP) 96
  73. Итоги. Kotlin/Native • Можно писать общую логику для Android и

    iOS на Котлине • А также компилироваться под другие нативные платформы • И взаимодействовать с C/Objective-C/Swift 97
  74. Примеры на Kotlin/Native • Репозиторий Kotlin/Native https://github.com/JetBrains/kotlin-native/tree/master/samples • Калькулятор для

    Android/iOS/JVM https://github.com/JetBrains/kotlin-native-calculator-sample • KotlinConf Spinner https://github.com/JetBrains/kotlinconf-spinner • Мультиплатформенный билд для Android/iOS https://github.com/JetBrains/kotlin-mpp-example 98
  75. Проекты • Мультиплатформенные корутины: kotlinx.coroutines https://github.com/Kotlin/kotlinx.coroutines/ • Мультиплатформенный I/O: kotlinx-io

    https://github.com/Kotlin/kotlinx-io • Игровой движок + полезные библиотеки https://github.com/korlibs/korlibs • Kotlin/Native обертка для libui https://github.com/msink/kotlin-libui 99
  76. Ссылки • Официальный сайт Котлина: https://kotlinlang.org/ • Примеры и Kotlin

    Koans: https://try.kotlinlang.org • Kotlin на GitHub: https://github.com/JetBrains/kotlin • Kotlin/Native на GitHub: https://github.com/JetBrains/kotlin-native • Slack: http://slack.kotlinlang.org 100