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

3fc5b5eb32bd3b48d7810fd67b37f9a1?s=47 Moscow JUG
November 14, 2019

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

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

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

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

3fc5b5eb32bd3b48d7810fd67b37f9a1?s=128

Moscow JUG

November 14, 2019
Tweet

Transcript

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

  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
  3. ARRIVAL Robotics a web app to connect mechanical designers and

    robofacturing collecting real-time feedback software defined robofacturing Kotlin ?: earlier TRA Robotics
  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
  5. What Δ do you expect?

  6. code is shorter more expressive have fun doing the migration

    less routine coding faster feature creation better code reading language features and stdlib
  7. Minuses?

  8. Minuses? yes :(

  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
  10. Current State

  11. None
  12. None
  13. Code Style

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

  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: <properties> <kotlin.code.style>official</kotlin.code.style> </properties> pom.xml kotlin.code.style=official gradle.properties
  17. Checkstyle

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

  19. Java 8 + Lombok -> Kotlin

  20. Equivalents @Getters @Setters properties @NonNull nullability @SneakyThrows only unchecked exceptions

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

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

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

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

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

    @Cleanup .use { … } val/var val/var @*ArgsConstructor primary constructor
  26. Complete Manual Implementation @ToString @EqualsAndHashCode @Synchronized @Builder

  27. @Data != data class

  28. Nullability

  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
  30. @Log val slf4j: ReadOnlyProperty<Any, Logger> get() = LoggerDelegate() class LoggerDelegate

    : ReadOnlyProperty<Any, Logger> { 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
  31. @Log val slf4j: ReadOnlyProperty<Any, Logger> get() = LoggerDelegate() class LoggerDelegate

    : ReadOnlyProperty<Any, Logger> { 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
  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”) } }
  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”) } }
  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
  35. @Log val slf4j: ReadOnlyProperty<Any, Logger> get() = LoggerDelegate() class LoggerDelegate

    : ReadOnlyProperty<Any, Logger> { 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
  36. @Log val slf4j: ReadOnlyProperty<Any, Logger> get() = LoggerDelegate() class LoggerDelegate

    : ReadOnlyProperty<Any, Logger> { 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
  37. Java to Kotlin Converter

  38. CTRL + ALT + SHIFT + K

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

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

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

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

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

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

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

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

    experimental workaround
  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
  48. Before we start

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

    functionality
  50. Keep semantics

  51. Let’s do it

  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" }
  53. No Code - No Problems

  54. Repository let’s do that

  55. @JvmDefault

  56. compileKotlin { kotlinOptions { freeCompilerArgs = ["-Xjvm-default=compatibility"] jvmTarget = "1.8"

    } } https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/index.html
  57. First migrations as small as possible but not smaller than

    a class the 1st avice
  58. Constants let’s do that

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

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

  61. Configs & Props let’s do that

  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
  63. First of all check docs docs of many libs/tools have

    a chapter called “Kotlin” the 3rd advice
  64. Aspects let’s do that

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

  66. Model let’s do that

  67. DTO & mappers let’s do that

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

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

    the 6th advice
  70. Services let’s do that

  71. Improve your code if changes are small but profit is

    great the 7rd advice
  72. Controllers let’s do that

  73. Check auto-generated* stuff manually if you are too lazy to

    write tests for it the 8th advice * not only, but at least
  74. Global Steps 1. What Δ do you expect? 2. Code

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

    Style / Checkstyle 3. Migrate 4. Review
  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
  77. Go in small steps if you really ready to solve

    problems the final avice
  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