O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Adotar o Kotlin para equipes grandes

O Kotlin é uma linguagem de programação que oferece uma sintaxe sucinta e vários recursos modernos, incluindo compatibilidade com lambda, gerenciamento de valores nulos como aspecto principal do sistema de tipo, compatibilidade de primeira classe com propriedades e muito mais. O Kotlin é compatível com muitos padrões de design nativos comuns, incluindo os padrões singleton e delegate.

Além dos recursos da linguagem, ele foi criado e é compatível com JetBrains, criadora do IntelliJ, que é a espinha dorsal do Android Studio (link em inglês). Essa integração ao ambiente de desenvolvimento integrado oferece muito suporte para o desenvolvimento do Kotlin, permitindo que suas equipes alcancem maior produtividade.

Kotlin no Android

A sintaxe concisa do Kotlin e a capacidade de estender facilmente as APIs existentes funcionam com APIs novas e atuais do Android. O Kotlin já faz parte de muitas das principais APIs do Android graças ao projeto de extensões do Android KTX. Além disso, muitos componentes Jetpack compatíveis com Java e Kotlin incluem uma versão KTX da biblioteca que fornece uma API idiomática para desenvolvedores Kotlin. Muitas bibliotecas, como a biblioteca Jetpack Benchmark, são criadas em Kotlin.

Simultaneidade

O Kotlin é compatível com programação assíncrona usando corrotinas. O Android também oferece compatibilidade de primeira classe para corrotinas do Kotlin em uma série de componentes de arquitetura, incluindo LiveData, ViewModel, Lifecycle e WorkManager. Para mais informações, consulte Usar corrotinas do Kotlin com componentes de arquitetura.

Ao considerar uma linguagem de programação em nível organizacional, pense na perspectiva de longo prazo. Atualmente, o Kotlin é a linguagem que mais cresce no mundo, de acordo com o SlashData (link em inglês).

Se você estava esperando o setor demonstrar uma tendência em direção ao Kotlin, este é o momento perfeito para aderir.

Caminho de migração

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. Isso geralmente é fácil, porque o Kotlin compila até o bytecode do 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. Há muitos recursos para dar os primeiros passos. 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 você aprendeu em um grupo ajuda a reforçar o material (link em inglês). Compre um livro de Kotlin ou outro material de estudo para cada membro do grupo e peça ao grupo para estudar alguns capítulos por semana (link em inglês). 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. De 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 quer 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 aplicativo único, na sua organização, em vez de 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 converter para o equivalente em Kotlin, como 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 e não de comportamento. Faça testes durante todo o 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 APK.

Mover métodos de utilitário 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 usadas pelo codebase Java atual.

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. Mas inicialmente, talvez você perceba que o código Kotlin que sua equipe produz se parece mais com o código Java que está substituindo. Isso muda com o tempo, à medida que a experiência da equipe com o Kotlin aumentar. 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 regras adicionais 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 bibliotecas novas e recursos sintáticos para reduzir o código clichê, o que ajuda a aumentar a velocidade de desenvolvimento. Dito isso, seja cauteloso e metódico 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 valores 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 compatibilidade integrada para implementar o comportamento por meio da delegação para ajudar a simplificar a composição (link em inglês).

Esse comportamento representa um problema para frameworks de simulação, como o Mockito, que dependem de implementação de interface ou herança para modificar comportamentos durante o teste. Para testes de unidade, você pode ativar o recurso Mock Maker Inline do Mockito, que permite simular classes e métodos finais. Como alternativa, você pode usar o plug-in para compilador All-Open para abrir qualquer classe Kotlin e os membros que você queira testar como parte do processo de compilação (links em inglês). A principal vantagem de usar esse plug-in é que ele funciona com testes instrumentados e de unidade.

Mais informações

Para mais informações sobre como usar o Kotlin, consulte os seguintes links: