Как с треском провалить миграцию с Java на Kotlin в Android приложении

https://blog.usejournal.com/how-to-fuck-up-java-to-kotlin-migration-in-your-existing-android-app-325b57c9ddbb
  • Перевод

Как с треском провалить миграцию с Java на Kotlin в Android приложении


С тех пор, как Google объявила об официальной поддержке Kotlin в Android, всё больше разработчиков хотят использовать его в своих новых и существующих проектах. Поскольку я также большой поклонник Kotlin, я не мог дождаться, когда смогу использовать Kotlin в своём рабочем проекте. В конце концов, Kotlin полностью совместим с Java, и все разработчики просто в восторге от этого. Так что же может пойти не так?


Ну, на самом деле многое может пойти не так. Меня просто пугает то, что на странице официальной документации Android «Начало работы с Kotlin» написано, что если вы хотите перенести существующее приложение на Kotlin, вы должны просто начать писать модульные тесты, а затем, после небольшого опыта работы с этим языком, вы должны писать новый код на Kotlin, а существующий Java-код просто конвертировать.


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


Познакомьтесь с моим маленьким проектом


У меня был проект, который я создал более года назад на Java, и мне нужно было немного его изменить и добавить один новый экран. Я подумал, что это отличная возможность проверить, действительно ли миграция существующего проекта на Kotlin очень проста, как все говорят. Исходя из рекомендаций официальной документации, я начал с написания на Kotlin модульных тестов. Новые классы я тоже писал на Kotlin и конвертировал некоторые существующие. Казалось, что всё замечательно, но через некоторое время я обнаружил одну неприятную проблему.


Kotlin + Lombok = :(


В моем приложении я использовал библиотеку Lombok для генерации геттеров и сеттеров. Lombok — это обработчик аннотаций для javac. К сожалению, компилятор kotlin использует javac без обработки аннотаций [источник]. Это означает, что методы, созданные при помощи Lombok, будут недоступны в Kotlin:


Методы, созданные при помощи Lombok, будут недоступны в Kotlin


Но вы можете сказать: «Хорошо, это будет не очень круто, но в принципе можно вручную создать геттеры и сеттеры для тех полей, которые будут необходимы в коде Kotlin. В конце концов, не нужно этого делать во всём проекте сразу».


Я подумал так же. Но с реальной проблемой столкнулся тогда, когда захотел добавить новый экран в приложение.


Kotlin + Lombok + Dagger 2 = :(((


В моём приложении используется Dagger 2 для внедрения зависимостей. При создании нового экрана я обычно создаю структуру MVP: Activity, Presenter, Component, Module и Contract. Все зависимости для презентора внедряются с помощью Dagger. Activity вызывает DaggerSomeComponent.builder().(...).build().inject(this), чтобы внедрить презентор с необходимыми для себя зависимостями.


Использование Dagger 2 вместе с Kotlin — не проблема. Только перед этим нужно применить kapt-плагин, который создает необходимые самогенерируемые классы для Dagger.


И вот здесь всё начинает разваливаться


Без kapt-плагина я не мог использовать сгенерированные Dagger-классы в файлах Kotlin. Но после того, как я добавил этот плагин, все методы, созданные Lombok, исчезли!


До применения kapt-плагина:


До применения kapt-плагина


После применения kapt-плагина:


После применения kapt-плагина


И, к сожалению, решения этой проблемы нет. Вы можете применять либо только kapt-плагин, либо только Lombok. К счастью, поскольку это был всего лишь мой маленький проект, я просто удалил Lombok и сам написал геттеры и сеттеры. Но в этом проекте было всего около 50 сгенерированных методов. В проекте, который мы поддерживаем на работе, у нас их около тысячи. Удаление Lombok из этого приложения просто невозможно.


Также, очевидно, что отказ от kapt-плагина — не выход из ситуации. Без него вы не сможете использовать Kotlin в классах, где используется Dagger. В моем случае я должен был бы реализовать Activity, Component и Module на Java, и только Contract и Presenter могли быть написаны на Kotlin. А смешивание файлов Java и Kotlin — это определенно не здорово. Вместо плавного перехода с Java на Kotlin вы просто создали бы большой беспорядок.


Вот так выглядела бы эта ужасная полиглотная MVP-структура без kapt-плагина:


Ужасная полиглотная MVP-структура без kapt-плагина


Но я всё же хочу перейти на Kotlin. Что мне делать?


Один из способов — использовать разные модули. Kotlin не увидит методы, которые Lombok будет генерировать в исходном коде, но будет видеть их в байт-коде.


Лично мне кажется, что это и есть самый предпочтительный путь. Если вы выводите Kotlin в отдельные зависимые модули для каждой фичи, вы уменьшаете риск таких проблем с совместимостью, с которыми столкнулся я, или более сложных, перечисленных в официальном гайде.


И это ещё не всё. В смешивании файлов Kotlin и Java без чёткого разделения есть много других недостатков. Это заставляет всех разработчиков, работающих с вами над проектом, знать оба языка. Также это уменьшает читаемость кода и может привести к увеличению времени сборки [источник].


Кратко


  • методы, сгенерированные Lombok, не видны в Kotlin;
  • использование kapt-плагина ломает Lombok;
  • без kapt-плагина вы не cможете использовать самогенерируемые классы для Dagger в Kotlin, что означает, что вам всё равно придётся писать новый код на Java;
  • способ решить эту проблему — вывести Kotlin в отдельные модули;
  • смешивание файлов Kotlin и Java в больших проектах без чёткого разделения может привести к неожиданным проблемам совместимости.
Поделиться публикацией
Комментарии 29
    +2
    Можно применить delombok и постепенно переписывать на kotlin.
      0
      А мне вот интересно зачем тривиальные геттеры/сеттеры?
        0
        безопасность обращения к переменным.
          –1
          И в чем же безопасность проявляется? Ну возьмем например класс Movie из второго рисунка. Чем тривиальный геттер безопаснее public final поля?
            +3

            Как вариант, поля не final, потому что где-то внутри класса модифицируются (мы ведь не видим весь класс, верно?), но снаружи менять их по очевидным причинам нельзя.

              +1
              final — ничем. Но только если final. Lombok все же неамного шире.
              0
              Вот интересно, что в C++ даже в Core Guidelines призывают не использовать геттеры/сеттеры, а использовать структуры (CoreGuidelines)
              В результате, в классах есть ну максимум один геттер/сеттер, а все остальное — это нормальные методы с нормальными именами, выполняющие нетривиальную функциональность. Неужели в яве с этим такие проблемы?
                0
                тоже понемногу двигаются в сторону property и подобного. давно двигаются.
              +2
              ИМХО, один из важнейших недостатков Java в сравнении с C# это отстутствие автопропертей. Весь этот геттеро-сеттерный бойлерплейт дико замусоривает классы и по размеру сравним иной раз с кодом, реализующим функциональность.
                +2

                В общем-то, это как раз одна из причин, почему люди выбирают Kotlin.

                  0
                  Проверил еще раз — в слове «Java» в своем комментарии ошибок не обнаружил.
                  0

                  В Kotlin есть data-классы, можно было воспользоваться ими)

                    0
                    обещают реализовать. давно уже обещают :)
                  +7

                  Мораль проста — не используйте Lombok. Я сейчас на большом проекте, где он используется, и за полгода им так и не проникся. В тривиальных случаях он помогает избавиться от нескольких строк бойлерплейта, но как-то это недостаточно весомая причина для +1 источника магии в коде.

                    0
                    Возможно в вашем проекте он не так уж и обходим.
                    Например у меня много микро-сервисов. В не которых из них могут быть поджо на десятки полей. Классы могут инжектиться до десятком (очень редко конечно) сервисов.
                    В первом случае писать толпу гетр сетеров во втором случае писать конструктор на несколько параметров и поддерживать его при изменениях кода.
                    А так же отличный фукционал генерации статик конструкторов и билдеров.
                    В целом на ломбок очень даже подходит на бэкенд задачах с толпой логики и энтетей.
                      0

                      Так ведь вместо кучки геттеров-сеттеров можно использовать public поля.

                        +1
                        писать толпу гетр сетеров

                        Вы так говорите, словно это проблема. В современных IDE есть генераторы геттеров/сеттеров и даже в простом текстовом редакторе с этой задачей на ура справится банальный поиск с заменой. Два у нас поля или двадцать особой роли не играет.

                      +1
                      Пример на скриншоте c Movie по идее должен отлично заменяться data классом
                        +1

                        Как я понимаю, автор воспользовался котлином как Java с улучшенным синтаксисом, без его плюшек, хотя я могу ошибаться, я не пишу на котлине.

                          0
                          Этот класс написан на джаве, не на котлине
                        0
                        В смешивании файлов Kotlin и Java без чёткого разделения есть много других недостатков.

                        Это каких? Могли бы привести конкретные примеры? Собственно для таких юзкейсов Котлин и создавался и отлично с ними работает.
                        Упомянутое замедление сборки может произойти конечно (из-за дополнительного анализа java кода компилятором Kotlin), но оно не критичное и на глаз его сложно заметить.


                        Это заставляет всех разработчиков, работающих с вами над проектом, знать оба языка.

                        Ну это не выглядит проблемой, без знания Java в Android разработке всяко никак, ну а обучить Java разработчика котлину на приемлемом уровне дело недели-двух

                          0

                          «pet project» — это не проект про домашних животных, это «детище, любимый проект». Хотя в контексте статьи ваш перевод этой фразы получился очень даже к месту))

                            0
                            Спасибо, поправили)
                            0
                            А сможете выложить этот игрушечный проект к нам в ишью-трекер (https://kotl.in/issue)?
                            Мы бы поисследовали и, вероятно, исправили бы этот баг (впрочем, есть подозрение в неверной конфигурации проекта).
                              0
                              Это перевод статьи, поэтому, к сожалению, нет такой возможности.

                              Напишите автору оригинальной статьи, думаю он с радостью это сделает.
                                0
                                Опять 25! Не видят люди никаких плашек! Мозг не приучен. Поди.
                              0
                              Ужас какой. Какие геттеры/сеттеры?
                              data class Movie(
                                  val id: Long,
                                  val title: String,
                                  val posterUrl: String
                              )
                              

                              Всё! У вашего entity теперь есть и get (чтобы были set нужно val поменять на var), а также equals(), hashcode(), toString() и clone(). Метод clone() — правильный подход. Должны быть лишь геттеры. Хочешь менять — создавай клон!
                              Перед тем, как перетаскивать проект с Java на Kotlin стоит Kotlin изучить.
                                0
                                статью не читай — комментарий пиши
                                –6
                                Вывод простой — к чёрту Котлин! Не будь стадом!
                                Завтра выйдет фуксия без котлина — куда пойдешь? в плюсы? в дарт?
                                или в ентерпрайз — обратно к jdk? или плакать-винить гугл и джет-брейнс что «приучили» и «бросили»?

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое