Переход на новый язык может быть непростым. Чтобы переход прошел успешно, начните с небольших объемов данных и часто тестируйте. Kotlin компилируется в байт-код JVM и полностью совместим с Java, что упрощает переход.
Создание команды
Первый шаг перед миграцией – сформировать у команды общее понимание. Вот несколько советов, которые помогут вам ускорить обучение команды.
Группы по изучению форм
Учебные группы – эффективный способ облегчить обучение и удержание знаний. Исследования показывают, что повторение изученного материала в группе помогает лучше его запомнить. Купите книгу по Kotlin или другие учебные материалы для каждого участника группы и попросите их проходить по паре глав в неделю. На каждой встрече группа должна сравнивать полученные знания и обсуждать вопросы и наблюдения.
Создайте культуру обучения
Не каждый считает себя учителем, но каждый может учить. Каждый может создать атмосферу, способствующую обучению и успеху, независимо от того, является ли он руководителем команды или отдельным сотрудником. Один из способов сделать это – проводить периодические презентации, на которых один из членов команды рассказывает о том, что он узнал или чем хочет поделиться. Вы можете попросить участников группы по очереди представлять новую главу каждую неделю, пока ваша команда не начнет чувствовать себя уверенно в использовании языка.
Назначьте ответственного за жетоны
Наконец, назначьте ответственного за обучение. Этот пользователь может выступать в качестве профильного эксперта (SME) на начальном этапе внедрения. Важно приглашать этого человека на все тренировочные встречи, связанные с Kotlin. В идеале этот человек уже должен быть увлечен Kotlin и иметь некоторые практические знания.
Постепенная интеграция
Начните с малого и тщательно продумайте, какие части экосистемы перенести в первую очередь. Чаще всего лучше выбрать одно приложение, а не флагманское. В каждом случае перенос выбранного приложения будет отличаться, но вот несколько распространенных вариантов, с которых можно начать.
Модель данных
Ваша модель данных, скорее всего, состоит из большого количества информации о состоянии и нескольких методов. В модели данных также могут быть общие методы, такие как toString, equals и hashCode. Обычно эти методы можно перенести и провести модульное тестирование отдельно.
Предположим, у вас есть следующий фрагмент кода на Java:
public class Person {
private String firstName;
private String lastName;
// ...
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
@Override
public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
Вы можете заменить класс Java одной строкой Kotlin, как показано в следующем примере:
data class Person(var firstName: String?, var lastName: String?)
После этого вы можете провести модульное тестирование этого кода с помощью текущего набора тестов. Начните с малого, перенося по одной модели за раз, и перенесите классы, которые в основном представляют состояние, а не поведение. Регулярно проводите тестирование.
Тесты переноса
Ещё один вариант – начать с тестов: преобразовать существующие и написать новые на Kotlin. Это даст вашей команде время освоиться с языком, прежде чем вы начнете писать код, который будет поставляться с вашим приложением.
Перенесите служебные методы в функции расширения
Статические служебные классы, такие как StringUtils, IntegerUtils, DateUtils или YourCustomTypeUtils, можно представить в виде функций расширения Kotlin и использовать их в существующей базе кода Java.
Например, у вас есть класс StringUtils с несколькими методами:
package com.java.project;
public class StringUtils {
public static String foo(String receiver) {
return receiver...; // Transform the receiver in some way
}
public static String bar(String receiver) {
return receiver...; // Transform the receiver in some way
}
}
Эти методы могут использоваться в других частях приложения, как показано в следующем примере:
...
String myString = ...
String fooString = StringUtils.foo(myString);
...
Используя функции расширения Kotlin, вы можете предоставить тот же интерфейс Utils вызывающим объектам Java, а также предложить более краткий API для растущей базы кода Kotlin.
Для этого можно начать с преобразования класса Utils в Kotlin с помощью автоматического преобразования, предоставляемого IDE. Пример выходных данных:
package com.java.project
object StringUtils {
fun foo(receiver: String): String {
return receiver...; // Transform the receiver in some way
}
fun bar(receiver: String): String {
return receiver...; // Transform the receiver in some way
}
}
Затем удалите определение класса или объекта, добавьте к имени каждой функции префикс с типом, к которому должна применяться эта функция, и используйте его для ссылки на тип внутри функции, как показано в следующем примере:
package com.java.project
fun String.foo(): String {
return this...; // Transform the receiver in some way
}
fun String.bar(): String {
return this...; // Transform the receiver in some way
}
Наконец, добавьте аннотацию JvmName в начало исходного файла, чтобы скомпилированное название было совместимо с остальной частью приложения, как показано в примере ниже.
@file:JvmName("StringUtils")
package com.java.project
...
Итоговая версия должна выглядеть примерно так:
@file:JvmName("StringUtils")
package com.java.project
fun String.foo(): String {
return this...; // Transform `this` string in some way
}
fun String.bar(): String {
return this...; // Transform `this` string in some way
}
Обратите внимание, что теперь эти функции можно вызывать с помощью Java или Kotlin, используя соглашения, соответствующие каждому языку.
Kotlin
// ... val myString: String = "hello" val fooString = myString.foo() // ...
Java
... String myString = ... String fooString = StringUtils.foo(myString); ...
Как выполнить перенос
Когда ваша команда освоится с Kotlin и вы перенесете небольшие области, можно будет перейти к более крупным компонентам, таким как фрагменты, действия, объекты ViewModel и другие классы, связанные с бизнес-логикой.
Что нужно учесть
Как и у Java, у Kotlin есть свой идиоматический стиль, который способствует лаконичности кода. Однако поначалу вы можете обнаружить, что код Kotlin, который пишет ваша команда, больше похож на код Java, который он заменяет. По мере того как ваша команда будет набираться опыта в Kotlin, это соотношение будет меняться. Постепенные изменения – ключ к успеху.
Вот несколько советов, которые помогут вам поддерживать единообразие базы кода Kotlin по мере ее роста:
Распространенные стандарты кодирования
Определите стандартный набор правил написания кода на ранних этапах внедрения. Вы можете отклоняться от руководства по стилю Kotlin для Android, если это целесообразно.
Инструменты статического анализа
Применяйте набор стандартов программирования, установленный для вашей команды, с помощью Android lint и других инструментов статического анализа. ktlint, сторонний линтер Kotlin, также предоставляет дополнительные правила для Kotlin.
Непрерывная интеграция
Убедитесь, что вы соблюдаете общие стандарты кодирования и обеспечиваете достаточное тестовое покрытие для своего кода Kotlin. Включение этой части в автоматизированный процесс сборки может помочь обеспечить согласованность и соблюдение этих стандартов.
Совместимость
Kotlin в основном совместим с Java, но следует учитывать следующие моменты.
Допускает значение NULL
Kotlin использует аннотации о допустимости значения NULL в скомпилированном коде, чтобы определять допустимость значения NULL на стороне Kotlin. Если аннотации не указаны, Kotlin по умолчанию использует тип платформы, который может быть как допускающим, так и не допускающим значение NULL. Если вы не будете осторожны с типами платформ, это может привести к проблемам во время выполнения.NullPointerException
Используйте новые функции
Kotlin предоставляет множество новых библиотек и синтаксический сахар, чтобы сократить шаблонный код и ускорить разработку. Однако при использовании функций стандартной библиотеки Kotlin, таких как функции коллекций, корутины и лямбды, следует проявлять осторожность и методичность.
Вот распространенная ловушка, в которую попадают разработчики, плохо знакомые с Kotlin. Рассмотрим следующий код Kotlin:
val nullableFoo: Foo? = Foo() // This lambda executes only if nullableFoo is not null // and `foo` is of the non-nullable Foo type nullableFoo?.let { foo -> foo.baz() foo.zap() }
В этом примере цель состоит в том, чтобы выполнить foo.baz и foo.zap, если nullableFoo не равно null, и избежать NullPointerException. Хотя этот код работает должным образом, его сложнее читать, чем проверку на null и умное приведение, как показано в следующем примере:
val nullableFoo: Foo? = null if (nullableFoo != null) { nullableFoo.baz() // Using !! or ?. isn't required; the Kotlin compiler infers non-nullability nullableFoo.zap() // from guard condition; smart casts nullableFoo to Foo inside this block }
Тестирование
В Kotlin классы и их функции по умолчанию закрыты для расширения. Классы и функции, которые вы хотите использовать в качестве подклассов, нужно открыть явным образом. Kotlin делает этот выбор, чтобы продвигать композицию вместо наследования. Чтобы упростить композицию, в Kotlin есть встроенная поддержка реализации поведения через делегирование.
Это создает проблемы для фреймворков имитации, таких как Mockito, которые полагаются на реализацию интерфейса или наследование для переопределения поведения во время тестирования. Для модульного тестирования можно включить функцию Mock Maker Inline библиотеки Mockito, которая позволяет имитировать конечные классы и методы. Вы также можете использовать плагин компилятора All-Open, чтобы открыть любой класс Kotlin и его элементы, которые вы хотите протестировать в процессе компиляции. Основное преимущество этого плагина заключается в том, что он работает как с модульными, так и с инструментальными тестами.
Дополнительная информация
Подробную информацию об использовании Kotlin можно найти по ссылкам ниже.
- Kotlin-first подход в Android
- Ресурсы для начала работы с Kotlin
- Ресурсы для пользователей Java, изучающих Kotlin
- Учебный план по переходу с Java на Kotlin – набор ресурсов, которые помогут программистам, пишущим на Java, изучить Kotlin и писать на нем идиоматический код.