Mudar para qualquer nova linguagem pode ser uma tarefa assustadora. A receita para o sucesso é começar devagar, fazer mudanças por partes e testar com frequência para alinhar a equipe em busca do sucesso. O Kotlin facilita a migração, porque se baseia no bytecode JVM e é totalmente interoperável com o Java.
Como montar a equipe
A primeira etapa antes da migração é criar um entendimento básico comum para a equipe. Veja algumas dicas que podem ser úteis para acelerar o aprendizado das equipes.
Formar grupos de estudo
Grupos de estudo são uma maneira eficaz de facilitar a aprendizagem e a absorção. Estudos sugerem que recitar o que aprendeu em um grupo ajuda a para reforçar o material. Compre um livro sobre Kotlin (link em inglês) ou outro material de estudo para cada participante e peça ao grupo para estudar alguns capítulos por semana. Durante cada reunião, o grupo pode comparar o que aprendeu e fazer perguntas ou observações.
Construir uma cultura de aprendizagem
Embora nem todo mundo se considere professor, todos podem transmitir conhecimento. Desde um líder de tecnologia ou equipe a um colaborador individual, todos podem incentivar um ambiente de aprendizagem que pode ajudar a garantir o sucesso. Uma maneira de facilitar isso é fazer apresentações periódicas, em que uma pessoa da equipe é designada para falar sobre algo que aprendeu ou que gostaria de compartilhar. Você pode aproveitar o grupo de estudo e pedir que voluntários apresentem um novo capítulo a cada semana até chegar a um ponto em que a equipe se sinta confortável com a linguagem.
Indicar uma liderança
Por fim, indique uma liderança para conduzir o processo de aprendizagem. Essa pessoa poderá atuar como um especialista no assunto (SME, na sigla em inglês) quando você iniciar o processo de adoção. É importante incluir essa pessoa em todas as reuniões práticas relacionadas ao Kotlin. O ideal é que essa pessoa já esteja envolvida com o Kotlin e tenha algum conhecimento prático.
Integrar lentamente
É fundamental começar devagar e pensar estrategicamente sobre quais partes do seu ecossistema precisam mudar primeiro. Muitas vezes, é melhor isolar esse processo em um app único na sua organização, em vez de em um app principal. Em termos de migração do app escolhido, cada situação é diferente, mas estes são alguns pontos comuns para começar.
Modelo de dados
Provavelmente, seu modelo de dados consiste em muitas informações de estado e alguns métodos. O modelo de dados também pode ter métodos comuns, como toString()
, equals()
e hashcode()
. Em geral, esses métodos podem ser transferidos e testados em isolamento com facilidade.
Por exemplo, considere o seguinte snippet 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 + '\'' +
'}';
}
}
Você pode substituir a classe Java por uma única linha de Kotlin, conforme mostrado aqui:
data class Person(var firstName: String?, var lastName : String?)
Esse código pode passar por um teste de unidade em relação ao seu conjunto de testes atual. A ideia aqui é começar aos poucos, com um modelo por vez e com as classes de transição que são principalmente de estado, não de comportamento. Faça testes com frequência ao longo do processo.
Migrar testes
Outro caminho inicial a ser considerado é converter os testes atuais e começar a criar novos testes em Kotlin. Assim, sua equipe tem tempo para se sentir confortável com a linguagem antes de escrever o código que você planeja enviar com o app.
Mover métodos utilitários para funções de extensão
Todas as classes de utilitários estáticos (StringUtils
, IntegerUtils
, DateUtils
YourCustomTypeUtils
e assim por diante) podem ser representadas como
Funções de extensão do Kotlin
e usados pela sua base de código Java já existente.
Por exemplo, considere que você tem uma classe StringUtils
com alguns 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
}
}
Esses métodos podem ser usados em outro lugar no app, como mostrado no exemplo a seguir:
...
String myString = ...
String fooString = StringUtils.foo(myString);
...
Usando as funções de extensão do Kotlin, você pode fornecer a mesma interface Utils
para autores de chamada do Java e, ao mesmo tempo, oferecer uma API mais sucinta para a crescente base do código Kotlin.
Para fazer isso, você pode começar convertendo essa classe Utils
em Kotlin usando a conversão automática fornecida pelo ambiente de desenvolvimento integrado. A saída de exemplo pode ser semelhante a esta:
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
}
}
Em seguida, remova a definição de classe ou objeto, prefixe cada nome de função com o tipo em que essa função se aplica e use-a para fazer referência ao tipo dentro da função, como mostrado no exemplo a seguir:
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 fim, adicione uma anotação JvmName
à parte superior do arquivo fonte para tornar o nome compilado compatível com o restante do app, como mostrado no exemplo a seguir:
@file:JvmName("StringUtils")
package com.java.project
...
A versão final se parecerá com esta:
@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
}
Essas funções agora podem ser chamadas usando Java ou Kotlin com convenções que correspondem a cada linguagem.
Kotlin
... val myString: String = ... val fooString = myString.foo() ...
Java
... String myString = ... String fooString = StringUtils.foo(myString); ...
Concluir a migração
Quando a equipe estiver familiarizada com o Kotlin e você tiver migrado áreas menores, poderá começar a processar componentes maiores, como fragmentos, atividades, objetos ViewModel
e outras classes relacionadas à lógica de negócios.
Considerações
Assim como o Java tem um estilo específico, o Kotlin tem seu próprio estilo idiomático que contribui para que ele seja sucinto. Inicialmente, no entanto, talvez você perceba que o código Kotlin que sua equipe produz se parece mais com o código Java que ele está substituindo. Isso muda com o tempo, à medida que a experiência da equipe com o Kotlin aumenta. Lembre-se de que a mudança gradual é a chave para o sucesso.
Veja algumas coisas que você pode fazer para conseguir consistência à medida que a base do código Kotlin crescer:
Padrões comuns de programação
Defina um conjunto padrão de convenções de programação logo no início do processo de adoção. Você pode divergir do guia de estilo Kotlin do Android onde fizer sentido.
Ferramentas de análise estática
Aplique os padrões de codificação definidos para sua equipe usando o Android lint e outras ferramentas de análise estática. O klint, um linter de Kotlin de terceiros, também fornece outras regras para Kotlin.
Integração contínua
Siga padrões de programação comuns e forneça cobertura de teste suficiente para o código Kotlin. Fazer isso, em um processo de criação automatizado, pode ajudar a garantir a consistência e a aderência a esses padrões.
Interoperabilidade
O Kotlin interopera com o Java perfeitamente na maior parte do tempo, mas observe o seguinte.
Nulidade
O Kotlin depende de anotações de nulidade no código compilado para inferir a capacidade de anulação no lado do Kotlin. Se as anotações não forem fornecidas, o Kotlin adotará, por padrão, um tipo de plataforma que pode ser tratado como o tipo anulável ou não anulável. No entanto, isso pode levar a problemas de NullPointerException
na execução se não for tratado com cuidado.
Adotar novos recursos
O Kotlin oferece muitas novas bibliotecas e o açúcar sintático para reduzir o código boilerplate, o que ajuda a aumentar o desenvolvimento velocidade Dito isso, tenha uma abordagem cautelosa e metódica ao usar as funções de biblioteca padrão do Kotlin, como funções de coleção, corrotinas e lambdas (links em inglês).
Aqui está uma armadilha muito comum que os desenvolvedores mais novos do Kotlin encontram. Considere o seguinte código Kotlin:
val nullableFoo: 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()
}
A intenção, neste exemplo, é executar foo.baz()
e foo.zap()
se
nullableFoo
não for nulo, evitando um NullPointerException
. Embora esse
código funcione como esperado, a leitura dele é menos intuitiva do que uma simples verificação de objetos nulos e
transmissão inteligente (link em inglês),
como mostrado no exemplo a seguir:
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
}
Teste
Por padrão, as classes e as funções são fechadas para extensão no Kotlin. É necessário abrir explicitamente as classes e funções de que você quer criar subclasses. Esse comportamento é uma decisão de design da linguagem escolhida para promover a composição em vez da herança. O Kotlin tem suporte integrado para implementar o comportamento usando delegação para ajudar a simplificar a composição.
Esse comportamento representa um problema para frameworks de simulação, como o Mockito, que dependem de implementação de interface ou herança para substituir comportamentos durante o teste. Para testes de unidade, é possível ativar o uso do elemento Mock Maker inline (em inglês) do Mockito, que permite simular classes e métodos finais. Como alternativa, use o Plug-in do compilador All-Open para abrir qualquer classe Kotlin e respectivos membros que você queira testar como parte do no processo de compilação. A principal vantagem de usar esse plug-in é que ele funciona com testes instrumentados e de unidade.
Mais informações
Para ver mais informações sobre como usar o Kotlin, confira os links a seguir:
- Abordagem Kotlin do Android
- Outros recursos para começar a usar o Kotlin
- Outros recursos para usuários do Java que estão aprendendo Kotlin
- Caminho de aprendizado Java para Kotlin, uma coleção de recursos que ajudam os programadores Java a aprender e escrever Kotlin idiomático.