Переход на любой новый язык программирования может быть непростой задачей. Рецепт успеха — начинать постепенно, двигаться поэтапно и часто тестировать, чтобы обеспечить согласованность действий команды. Kotlin упрощает миграцию, поскольку компилируется в байт-код JVM и полностью совместим с Java.
Формирование команды
The first step before migrating is to build a common baseline understanding for your team. Here are a few tips that you might find useful to accelerate your team's learning.
Сформируйте учебные группы.
Учебные группы — эффективный способ обучения и запоминания материала. Исследования показывают , что совместное повторение изученного в группе помогает закрепить знания. Приобретите каждому члену группы книгу по Kotlin или другие учебные материалы и попросите группу каждую неделю разбирать по несколько глав. На каждой встрече группа должна сравнивать полученные знания и обсуждать любые вопросы или замечания.
Создайте культуру преподавания.
Хотя не каждый считает себя учителем, учить может каждый. От руководителя технологического отдела или команды до рядового сотрудника — каждый может способствовать созданию учебной среды, которая поможет обеспечить успех. Один из способов это сделать — проводить периодические презентации, на которых один из членов команды рассказывает о том, чему он научился или чем хочет поделиться. Вы можете использовать свою учебную группу, попросив добровольцев каждую неделю представлять новую главу, пока ваша команда не почувствует себя уверенно в использовании языка.
Назначьте чемпиона
Наконец, назначьте ответственного за обучение. Этот человек может выступать в роли эксперта в предметной области на начальном этапе внедрения. Важно включать этого человека во все ваши практические встречи, посвященные Kotlin. В идеале, этот человек уже увлечен Kotlin и обладает определенными практическими знаниями.
Внедряйте постепенно.
Ключевым моментом является постепенное начало и стратегическое планирование того, какие части вашей экосистемы следует перенести в первую очередь. Зачастую лучше ограничиться одним приложением в вашей организации, а не флагманским приложением. Что касается миграции выбранного приложения, каждая ситуация индивидуальна, но вот несколько распространенных отправных точек.
модель данных
Ваша модель данных, вероятно, содержит много информации о состоянии, а также несколько методов. Модель данных также может включать общие методы, такие как toString() , equals() и hashcode() . Эти методы обычно легко тестируются и могут быть протестированы в изолированном режиме.
For example, assume the following snippet of 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 + '\'' +
'}';
}
}
You can replace the Java class with a single line of Kotlin, as shown here:
data class Person(var firstName: String?, var lastName: String?)
Затем этот код можно протестировать с помощью модульных тестов, используя ваш текущий набор тестов. Идея состоит в том, чтобы начать с малого, с одной модели за раз, и постепенно переходить к классам, которые в основном содержат состояние, а не поведение. Не забывайте часто проводить тестирование на протяжении всего процесса.
Миграционные тесты
Ещё один вариант для начала — перевести существующие тесты на Kotlin и начать писать новые. Это даст вашей команде время освоиться с языком, прежде чем писать код, который вы планируете включить в своё приложение.
Переместите вспомогательные методы в функции расширения.
Any static utility classes ( StringUtils , IntegerUtils , DateUtils , YourCustomTypeUtils , and so on) can be represented as Kotlin extension functions and used by your existing Java codebase.
For example, consider you have a StringUtils class with a few methods:
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
}
}
These methods might then be used elsewhere in your app, as shown in the following example:
...
String myString = ...
String fooString = StringUtils.foo(myString);
...
Using Kotlin extension functions, you can provide the same Utils interface to Java callers while at the same time offering a more succinct API for your growing Kotlin code base.
To do this, you could start by converting this Utils class to Kotlin using the automatic conversion provided by the IDE. Example output might look similar to the following:
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
}
Finally, add a JvmName annotation to the top of the source file to make the compiled name compatible with the rest of your app, as shown in the following example:
@file:JvmName("StringUtils")
package com.java.project
...
The final version should look similar to the following:
@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
}
Note that these functions can now be called using Java or Kotlin with conventions that match each language.
Котлин
// ... val myString: String = "hello" val fooString = myString.foo() // ...
Java
... String myString = ... String fooString = StringUtils.foo(myString); ...
Завершите миграцию
Как только ваша команда освоит Kotlin и вы перенесете небольшие фрагменты кода, вы можете перейти к работе над более крупными компонентами, такими как фрагменты, активности, объекты ViewModel и другие классы, связанные с бизнес-логикой.
Соображения
Подобно тому, как Java имеет свой специфический стиль, Kotlin обладает собственным идиоматическим стилем, который способствует его лаконичности. Однако поначалу вы можете обнаружить, что код на Kotlin, создаваемый вашей командой, больше похож на код на Java, который он заменяет. Со временем это меняется по мере того, как опыт вашей команды в Kotlin растет. Помните, что постепенные изменения — ключ к успеху.
Here are a few things you can do to attain consistency as your Kotlin code base grows:
Общие стандарты кодирования
Be sure to define a standard set of coding conventions early on in your adoption process. You can diverge from the Android Kotlin style guide where it makes sense.
Инструменты статического анализа
Enforce the coding standards set for your team by using Android lint and other static analysis tools. klint , a third-party Kotlin linter, also provides additional rules for Kotlin.
Непрерывная интеграция
Обязательно соблюдайте общепринятые стандарты кодирования и обеспечьте достаточное тестовое покрытие для вашего кода на Kotlin. Включение этого в автоматизированный процесс сборки поможет обеспечить согласованность и соответствие этим стандартам.
Взаимодействие
Kotlin interoperates with Java seamlessly for the most part, but note the following.
Недопустимость
Kotlin использует аннотации, указывающие на возможность значения null, в скомпилированном коде для определения возможности значения null на стороне Kotlin. Если аннотации не указаны, Kotlin по умолчанию использует тип платформы, который может рассматриваться как допускающий или не допускающий значение null. Однако это может привести к ошибкам NullPointerException во время выполнения, если не принять соответствующие меры.
Внедрение новых функций
Kotlin предоставляет множество новых библиотек и синтаксического сахара для уменьшения шаблонного кода, что способствует ускорению разработки. Однако следует проявлять осторожность и методичность при использовании стандартных библиотечных функций Kotlin, таких как функции коллекций , сопрограммы и лямбда-выражения .
Here's a very common trap that newer Kotlin developers encounter. Assume the following Kotlin code:
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 имеет встроенную поддержку реализации этого поведения посредством делегирования , что упрощает композицию.
Такое поведение создает проблему для фреймворков для создания моков, таких как Mockito, которые полагаются на реализацию интерфейса или наследование для переопределения поведения во время тестирования. Для модульных тестов можно включить функцию Mock Maker Inline в Mockito, которая позволяет создавать моки для финальных классов и методов. В качестве альтернативы можно использовать плагин компилятора All-Open для открытия любого класса Kotlin и его членов, которые вы хотите протестировать, в процессе компиляции. Главное преимущество использования этого плагина заключается в том, что он работает как с модульными, так и с инструментальными тестами.
Более подробная информация
For more information on using Kotlin, check out the following links:
- Подход Android, ориентированный на Kotlin.
- Ресурсы для начала работы с Kotlin
- Ресурсы для пользователей Java, изучающих Kotlin.
- Java to Kotlin learning pathway , a collection of resources that help Java programmers learn and write idiomatic Kotlin.