Pasar a un lenguaje nuevo puede ser un desafío. Para preparar a tu equipo para el éxito, comienza despacio, migra en partes pequeñas y realiza pruebas con frecuencia. Kotlin se compila en bytecode de JVM y es completamente interoperable con Java, lo que facilita la migración.
Cómo crear el equipo
El primer paso antes de la migración es analizar el modelo de referencia común de tu equipo. Estas son algunas sugerencias que pueden resultarte útiles para acelerar el aprendizaje de tu equipo.
Crea grupos de estudio
Los grupos de estudio son un método eficaz para facilitar el aprendizaje y la retención. Los estudios sugieren que recitar lo que aprendiste en un entorno grupal ayuda a reforzar el material. Consigue un libro de Kotlin u otro material de estudio para cada miembro del grupo y pide al grupo que revise un par de capítulos cada semana. Durante cada reunión, el grupo debe comparar lo que aprendió y debatir cualquier observación o pregunta.
Desarrolla una cultura de enseñanza
Todos pueden enseñar aunque no se consideren profesores. Todos pueden fomentar un entorno de aprendizaje que ayude a garantizar el éxito, desde un líder de tecnología o de equipo hasta un colaborador individual. Una manera de facilitar esto es realizar presentaciones periódicas en las que una persona del equipo esté designada para hablar sobre algún tema que haya aprendido o desee compartir. Puedes aprovechar tu grupo de estudio si pides voluntarios para presentar un nuevo capítulo cada semana hasta llegar a un punto en el que el equipo se sienta cómodo con el lenguaje.
Designa a un coordinador
Por último, designa a un coordinador para que lidere el aprendizaje. Esa persona puede actuar como experto en la materia (SME) mientras comienzas el proceso de adopción. Es importante incluir a esa persona en todas tus reuniones de práctica relacionadas con Kotlin. Idealmente, esa persona ya debería dominar Kotlin y tener conocimientos prácticos.
Haz la integración sin apuros
La clave está en comenzar sin apuro y pensar de forma estratégica qué partes de tu ecosistema mover primero. A menudo, es mejor aislar esto en una sola app dentro de tu organización en lugar de una app insignia. En términos de migrar la app elegida, cada situación es diferente, pero estos son algunos lugares comunes para comenzar.
Modelo de datos
Es probable que el modelo de datos incluya mucha información de estado junto con algunos métodos. El modelo de datos también puede tener métodos comunes, como toString, equals y hashCode. Por lo general, puedes migrar estos métodos y realizar pruebas de unidades de forma aislada.
Por ejemplo, echa un vistazo al siguiente fragmento de 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 + '\'' +
'}';
}
}
Puedes reemplazar la clase Java con una sola línea de Kotlin, como se muestra en el siguiente ejemplo:
data class Person(var firstName: String?, var lastName: String?)
Luego, puedes realizar pruebas unitarias de este código en tu paquete de pruebas actual. Comienza con un modelo a la vez y migra las clases que son principalmente de estado en lugar de comportamiento. Realiza pruebas con frecuencia en el camino.
Migra pruebas
Otro lugar para comenzar son tus pruebas: convierte las pruebas existentes y escribe pruebas nuevas en Kotlin. Esto le da tiempo a tu equipo para que se familiarice con el lenguaje antes de escribir el código que se envía con tu app.
Mueve métodos de utilidad a funciones de extensión
Puedes representar clases de utilidad estáticas, como StringUtils,
IntegerUtils, DateUtils, o YourCustomTypeUtils, como
funciones de extensión de Kotlin
y usarlas desde tu base de código Java existente.
Por ejemplo, supongamos que tienes una clase StringUtils con algunos métodos:
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
}
}
Estos métodos se pueden usar en otra parte de tu app, como se muestra en el siguiente ejemplo:
...
String myString = ...
String fooString = StringUtils.foo(myString);
...
Con las funciones de extensión de Kotlin, puedes proporcionar la misma interfaz Utils a las personas que llaman a Java y, al mismo tiempo, ofrecer una API más concisa para tu base de código Kotlin en crecimiento.
Para ello, puedes comenzar por convertir esa clase Utils en Kotlin mediante la conversión automática que proporciona el IDE. El resultado de ejemplo podría ser similar al siguiente:
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
}
}
A continuación, quita la definición de clase u objeto; coloca el tipo en el que debe aplicarse la función como prefijo del nombre de cada función; y utilízalo para hacer referencia al tipo que se encuentra dentro de la función, como se muestra en el siguiente ejemplo:
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
}
Por último, agrega una anotación JvmName en la parte superior del archivo fuente para que el nombre compilado sea compatible con el resto de tu app, como se muestra a continuación:
@file:JvmName("StringUtils")
package com.java.project
...
La versión final debería ser similar a la siguiente:
@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
}
Ten en cuenta que ahora es posible llamar a esas funciones utilizando Java o Kotlin con convenciones que sean compatibles con cada lenguaje.
Kotlin
// ... val myString: String = "hello" val fooString = myString.foo() // ...
Java
... String myString = ... String fooString = StringUtils.foo(myString); ...
Completa la migración
Una vez que tu equipo se sienta cómodo con Kotlin y hayas migrado áreas más pequeñas, puedes pasar a componentes más grandes, como fragmentos, actividades, objetos ViewModel y otras clases relacionadas con la lógica empresarial.
Consideraciones
Así como Java tiene un estilo específico, Kotlin tiene su propio estilo idiomático que contribuye a su concisión. Sin embargo, al principio, es posible que el código Kotlin que escriba tu equipo se parezca más al código Java que reemplaza. Esto cambia a medida que tu equipo adquiere experiencia en Kotlin. El cambio gradual es la clave del éxito.
Estas son algunas acciones que puedes realizar para mantener la coherencia de tu base de código Kotlin a medida que crece:
Estándares de codificación comunes
Asegúrate de definir un conjunto estándar de convenciones de codificación al comienzo del proceso de adopción. Puedes distanciarte de la guía de estilo de Kotlin de Android cuando sea conveniente.
Herramientas de análisis estático
Aplica los estándares de codificación establecidos para tu equipo con Android lint y otras herramientas de análisis estático. ktlint, un linter de Kotlin de terceros, también proporciona reglas adicionales para Kotlin.
Integración continua
Asegúrate de cumplir con los estándares de codificación comunes y brinda una cobertura de prueba suficiente para tu código Kotlin. Hacer esto como parte de un proceso de compilación automático puede ayudar a garantizar la coherencia y el cumplimiento de estos estándares.
Interoperabilidad
Kotlin interopera con Java sin problemas en su mayor parte, pero ten en cuenta los siguientes puntos.
Nulabilidad
Kotlin se basa en anotaciones de nulabilidad en el código compilado para inferir la nulabilidad del lado de Kotlin. Si no se proporcionan anotaciones, Kotlin usa de forma predeterminada un tipo de plataforma que se puede tratar como anulable o no anulable. Si no manejas los tipos de plataforma con cuidado, esto puede generar problemas de NullPointerException en el tiempo de ejecución.
Adopta nuevas funciones
Kotlin proporciona muchas bibliotecas nuevas y azúcar sintáctico para reducir el código estándar, lo que ayuda a aumentar la velocidad de desarrollo. Dicho esto, sé prudente y metódico cuando uses las funciones de biblioteca estándares de Kotlin, como funciones de colección, corrutinas, y lambdas.
Esta es una trampa común en la que caen los desarrolladores que son nuevos en Kotlin. Considera el siguiente código 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() }
El objetivo de este ejemplo es ejecutar foo.baz y foo.zap si nullableFoo no es nulo, lo que evita una NullPointerException. Si bien este
código funciona como se espera, es menos intuitivo de leer que una verificación de nulo y
una conversión inteligente,
como se muestra en el siguiente ejemplo:
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 }
Pruebas
En Kotlin, las clases y sus funciones se cierran para la extensión de forma predeterminada. Debes abrir de forma explícita las clases y funciones que deseas crear como subclases. Kotlin toma esta decisión para promover la composición sobre la herencia. Para ayudar a simplificar la composición, Kotlin tiene compatibilidad integrada para implementar el comportamiento a través de la delegación.
Ese comportamiento genera un problema para los frameworks ficticios, como Mockito, que dependen de la implementación de la interfaz o de la herencia para anular comportamientos durante las pruebas. Para las pruebas de unidades, puedes habilitar el uso de la función Mock Maker Inline de Mockito, que te permite simular clases y métodos finales. Como alternativa, puedes usar el complemento del compilador All-Open para abrir cualquier clase de Kotlin y sus miembros que desees probar como parte del proceso de compilación. La principal ventaja de usar este complemento es que funciona tanto con las pruebas de unidad como con las pruebas de instrumentación.
Más información
Para obtener más información sobre el uso de Kotlin, consulta los siguientes vínculos:
- Enfoque de prioridad de Kotlin en Android
- Recursos para comenzar a usar Kotlin
- Recursos para usuarios de Java que aprenden a usar Kotlin
- Ruta de aprendizaje de Java a Kotlin: Es una colección de recursos que ayuda a los programadores de Java a aprender y escribir código Kotlin idiomático.