$30 off During Our Annual Pro Sale. View Details »

Иван Осипов — Из Java на Kotlin, сидя на корме веб-приложения

Moscow JUG
November 14, 2019

Иван Осипов — Из Java на Kotlin, сидя на корме веб-приложения

Kotlin имеет место не только на поприще android разработки, но и в backend мире. Много кода уже написано на Java и Spring Boot, что же со всем этим делать?

Мигрировать! В ходе доклада мы проделаем путь с прекрасного "острова Java" со своими дикими зверьми вроде lombok в направлении "острова Kotlin", где постараемся адаптироваться и оглянуться назад.

Шаг за шагом мы мигрируем веб приложение с одного языка на другой, а по пути взглянем на местами неочевидные проблемы.

Moscow JUG

November 14, 2019
Tweet

More Decks by Moscow JUG

Other Decks in Programming

Transcript

  1. From Java to Kotlin
    sitting aft on a web app

    View Slide

  2. github.com/ivan-osipov/repository
    telegram ivan_osipov
    twitter _osipov_
    name Ivan Osipov
    viber > sudo rm -rf
    whatsapp > dev/null
    deprecated
    i-osipov.ru
    t.me/from_junior_to_senior
    from-java-to-kotlin

    View Slide

  3. ARRIVAL Robotics
    a web app to connect
    mechanical designers
    and robofacturing
    collecting
    real-time feedback
    software defined
    robofacturing
    Kotlin ?:
    earlier TRA Robotics

    View Slide

  4. Agenda
    1. Motivation
    2. Preparation (checkstyle, autoformatting, test coverage)
    3. Java + Lombok vs. Kotlin
    4. Java to Kotlin Converter
    5. Bugs, rakes and steps to migrate

    View Slide

  5. What Δ do you expect?

    View Slide

  6. code is shorter
    more expressive
    have fun doing the migration
    less routine coding
    faster feature creation
    better code reading
    language features and stdlib

    View Slide

  7. Minuses?

    View Slide

  8. Minuses?
    yes :(

    View Slide

  9. lost ternary operator
    lost a number of ide features
    structural search, AspectJ highlights
    nullability noize!!
    lost implicit widening conversions
    companion object {
    noise
    }
    third party idiomaticity

    View Slide

  10. Current State

    View Slide

  11. View Slide

  12. View Slide

  13. Code Style

    View Slide

  14. View Slide

  15. https://kotlinlang.org/docs/reference/code-style-migration-guide.html
    1 Oct. 2018
    Kotlin Coding Conventions

    View Slide

  16. https://kotlinlang.org/docs/reference/code-style-migration-guide.html
    1 Oct. 2018
    Kotlin Coding Conventions
    ● Since Kotlin 1.3 by default for new projects
    ● For old projects:

    official

    pom.xml
    kotlin.code.style=official
    gradle.properties

    View Slide

  17. Checkstyle

    View Slide

  18. https://github.com/arturbosch/detekt
    https://github.com/pinterest/ktlint

    View Slide

  19. Java 8 + Lombok -> Kotlin

    View Slide

  20. Equivalents
    @Getters
    @Setters
    properties
    @NonNull nullability
    @SneakyThrows only unchecked exceptions
    @Cleanup .use { … }
    val/var val/var
    @*ArgsConstructor primary constructor

    View Slide

  21. Equivalents
    @Getters
    @Setters
    properties
    @NonNull nullability
    @SneakyThrows only unchecked exceptions
    @Cleanup .use { … }
    val/var val/var
    @*ArgsConstructor primary constructor

    View Slide

  22. Equivalents
    @Getters
    @Setters
    properties
    @NonNull nullability
    @SneakyThrows only unchecked exceptions
    @Cleanup .use { … }
    val/var val/var
    @*ArgsConstructor primary constructor

    View Slide

  23. Equivalents
    @Getters
    @Setters
    properties
    @NonNull nullability
    @SneakyThrows only unchecked exceptions
    @Cleanup .use { … }
    val/var val/var
    @*ArgsConstructor primary constructor

    View Slide

  24. Equivalents
    @Getters
    @Setters
    properties
    @NonNull nullability
    @SneakyThrows only unchecked exceptions
    @Cleanup .use { … }
    val/var val/var
    @*ArgsConstructor primary constructor

    View Slide

  25. Equivalents
    @Getters
    @Setters
    properties
    @NonNull nullability
    @SneakyThrows only unchecked exceptions
    @Cleanup .use { … }
    val/var val/var
    @*ArgsConstructor primary constructor

    View Slide

  26. Complete Manual Implementation
    @ToString @EqualsAndHashCode
    @Synchronized @Builder

    View Slide

  27. @Data != data class

    View Slide

  28. Nullability

    View Slide

  29. @Log
    @Slf4j
    public class MyService {
    public void doSmth() {
    log.info(“my log entry”)
    }
    }
    class MyService {
    val log by slf4j
    fun doSmth() {
    log.info(“my log entry”)
    }
    }
    https://discuss.kotlinlang.org/t/best-practices-for-loggers/226/26

    View Slide

  30. @Log
    val slf4j: ReadOnlyProperty
    get() = LoggerDelegate()
    class LoggerDelegate : ReadOnlyProperty {
    lateinit var logger: Logger
    override fun getValue(thisRef: Any, property: KProperty<*>): Logger {
    if (!::logger.isInitialized) logger = LoggerFactory.getLogger(thisRef.javaClass)
    return logger
    }
    }
    https://discuss.kotlinlang.org/t/best-practices-for-loggers/226/26

    View Slide

  31. @Log
    val slf4j: ReadOnlyProperty
    get() = LoggerDelegate()
    class LoggerDelegate : ReadOnlyProperty {
    lateinit var logger: Logger
    override fun getValue(thisRef: Any, property: KProperty<*>): Logger {
    if (!::logger.isInitialized) logger = LoggerFactory.getLogger(thisRef.javaClass)
    return logger
    }
    }
    https://discuss.kotlinlang.org/t/best-practices-for-loggers/226/26

    View Slide

  32. @Log
    @Slf4j
    public class MyService {
    public void doSmth() {
    log.info(“my log entry”)
    }
    }
    class MyService {
    val log by slf4j
    fun doSmth() {
    log.info(“my log entry”)
    }
    }

    View Slide

  33. @Log
    @Slf4j
    public class MyService {
    public void doSmth() {
    log.info(“my log entry”)
    }
    }
    class MyService {
    companion object {
    val log by slf4j
    }
    fun doSmth() {
    log.info(“my log entry”)
    }
    }

    View Slide

  34. @Log
    @Slf4j
    public class MyService {
    public void doSmth() {
    log.info(“my log entry”)
    }
    }
    class MyService {
    companion object {
    val log by slf4j
    }
    fun doSmth() {
    log.info(“my log entry”)
    }
    }
    https://www.reddit.com/r/Kotlin/comments/8gbiul/slf4j_loggers_in_3_ways
    INFO my.package.MyService$Companion - my log entry

    View Slide

  35. @Log
    val slf4j: ReadOnlyProperty
    get() = LoggerDelegate()
    class LoggerDelegate : ReadOnlyProperty {
    lateinit var logger: Logger
    override fun getValue(thisRef: Any, property: KProperty<*>): Logger {
    if (!::logger.isInitialized) {
    val javaClass = thisRef.javaClass
    logger = LoggerFactory.getLogger(
    javaClass
    )
    }
    return logger
    }
    }
    https://discuss.kotlinlang.org/t/best-practices-for-loggers/226/26

    View Slide

  36. @Log
    val slf4j: ReadOnlyProperty
    get() = LoggerDelegate()
    class LoggerDelegate : ReadOnlyProperty {
    lateinit var logger: Logger
    override fun getValue(thisRef: Any, property: KProperty<*>): Logger {
    if (!::logger.isInitialized) {
    val javaClass = thisRef.javaClass
    logger = LoggerFactory.getLogger(
    if (javaClass.kotlin.isCompanion) javaClass.enclosingClass else javaClass
    )
    }
    return logger
    }
    }
    https://discuss.kotlinlang.org/t/best-practices-for-loggers/226/26

    View Slide

  37. Java to Kotlin Converter

    View Slide

  38. CTRL + ALT + SHIFT + K

    View Slide

  39. System.out.println(
    Integer.toHexString(
    0xFF000000
    )
    )
    FF000000
    0
    Compile error

    View Slide

  40. FF000000
    0
    Compile error
    System.out.println(
    Integer.toHexString(
    0xFF000000
    )
    )

    View Slide

  41. println(
    Integer.toHexString(
    -0x1000000
    )
    )
    Java to Kotlin Converter

    View Slide

  42. println(
    Integer.toHexString(
    0xFF000000
    )
    )
    FF000000
    0
    Compile error
    Rewritten

    View Slide

  43. FF000000
    0
    Compile error
    println(
    Integer.toHexString(
    0xFF000000
    )
    )
    Rewritten

    View Slide

  44. FF000000
    0
    Compile error
    println(
    Integer.toHexString(
    0xFF000000
    )
    )
    Rewritten
    Long
    https://discuss.kotlinlang.org/t/when-does-bit-fiddling-come-to-kotlin/2249

    View Slide

  45. FF000000
    0
    Compile error
    println(
    Integer.toHexString(
    0xFF000000.toInt()
    )
    )
    Rewritten
    workaround

    View Slide

  46. FF000000
    0
    Compile error
    println(
    Integer.toHexString(
    0xFF000000u.toInt()
    )
    )
    Rewritten
    experimental workaround

    View Slide

  47. FF000000
    0
    Compile error
    println(
    Integer.toHexString(
    0xFF000000u
    )
    )
    Rewritten
    a future solution?
    https://discuss.kotlinlang.org/t/when-does-bit-fiddling-come-to-kotlin/2249

    View Slide

  48. Before we start

    View Slide

  49. No tests - No migrations
    at least cover your core functionality

    View Slide

  50. Keep semantics

    View Slide

  51. Let’s do it

    View Slide

  52. Gradle Plugins
    plugins {
    ...
    id 'org.jetbrains.kotlin.jvm' version "1.3.50"
    id "org.jetbrains.kotlin.plugin.spring" version "1.3.50"
    id "org.jetbrains.kotlin.plugin.allopen" version "1.3.50"
    }

    View Slide

  53. No Code - No Problems

    View Slide

  54. Repository
    let’s do that

    View Slide

  55. @JvmDefault

    View Slide

  56. compileKotlin {
    kotlinOptions {
    freeCompilerArgs = ["-Xjvm-default=compatibility"]
    jvmTarget = "1.8"
    }
    }
    https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/index.html

    View Slide

  57. First migrations as small as possible
    but not smaller than a class
    the 1st avice

    View Slide

  58. Constants
    let’s do that

    View Slide

  59. @JvmField
    const val
    @file:JvmName(“...”)
    object … {
    ...
    }

    View Slide

  60. Keep backward compatibility
    even inside your team
    the 2nd advice

    View Slide

  61. Configs & Props
    let’s do that

    View Slide

  62. https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html
    #boot-features-kotlin-configuration-properties
    @ConfigurationProperties when used in combination with @ConstructorBinding
    supports classes with immutable val properties
    since 2.2.0

    View Slide

  63. First of all check docs
    docs of many libs/tools have a chapter called “Kotlin”
    the 3rd advice

    View Slide

  64. Aspects
    let’s do that

    View Slide

  65. The Converter Is Software
    can have bugs
    the 4rd advice

    View Slide

  66. Model
    let’s do that

    View Slide

  67. DTO & mappers
    let’s do that

    View Slide

  68. Check compatibility of your libs with Kotlin
    before migration!
    the 5th advice

    View Slide

  69. Be strong. Someday it will end
    probably not this month
    the 6th advice

    View Slide

  70. Services
    let’s do that

    View Slide

  71. Improve your code
    if changes are small but profit is great
    the 7rd advice

    View Slide

  72. Controllers
    let’s do that

    View Slide

  73. Check auto-generated* stuff manually
    if you are too lazy to write tests for it
    the 8th advice
    * not only, but at least

    View Slide

  74. Global Steps
    1. What Δ do you expect?
    2. Code Style / Checkstyle
    3. Migrate
    4. Review

    View Slide

  75. Global Steps
    1. What Δ do you expect?
    2. Code Style / Checkstyle
    3. Migrate
    4. Review

    View Slide

  76. Migration Steps
    1. Repositories
    2. Constants
    3. Config & Props
    4. Delombok everything (at least one sub domain)
    5. From the most abstract entity classes downwardly
    6. Subdomains: Model + DTO + Mappers
    7. Services & Another Domain Logic
    8. Controllers

    View Slide

  77. Go in small steps
    if you really ready to solve problems
    the final avice

    View Slide

  78. github.com/ivan-osipov/repository
    telegram ivan_osipov
    twitter _osipov_
    name Ivan Osipov
    viber > sudo rm -rf
    whatsapp > dev/null
    deprecated
    i-osipov.ru
    t.me/from_junior_to_senior
    from-java-to-kotlin

    View Slide