Type alias e inline class em Kotlin

O que são e como se diferenciam

Diego Malone
Accenture Digital Product Dev
5 min readDec 3, 2019

--

Type Alias

Durante o desenvolvimento de sistemas, não é muito raro acontecer de termos duas classes com o mesmo nome em diferentes pacotes. Isso pode rolar por diversos motivos, como um tipo definido em uma biblioteca de terceiros que você está usando, por exemplo, ou por herança de algum tipo legado.

Vamos supor que você está desenvolvendo em um sistema que tem referências para uma classe Customer, que já faz parte do sistema e já é utilizada em outras partes, e por isso você não pode renomeá-la. Porém, você precisou criar um tipo Customer e isso tem causado confusão para saber qual é qual.

Em Kotlin existem algumas formas de utilizar classes com o mesmo nome em um arquivo. O mais direto é utilizar a referência completa da classe (com o pacote) em todo lugar que for utilizar.

Utilização do nome completo da classe, com o pacote, no meio do código

Porém, melhor que isso, existe a possibilidade de realizar o import do tipo legado utilizando a palavra reservada as para renomear um tipo e evitar os dois tipos com o mesmo nome.

Renomeando um tipo legado no import com a palavra reservada as

Para fazer isso, porém, temos que lembrar de colocar o as em todos os lugares que usarem o tipo legado, o que pode ser esquecido e continuar causando confusões.

Para facilitar, no Kotlin podemos criar apelidos (chamados aqui de alias) usando a palavra reservada typealias. Assim, podemos criar um novo nome para o tipo legado e utilizar esse nome em todo o projeto sem se preocupar com o import. Você pode definir um novo alias em qualquer parte do código em que você pode definir uma nova classe.

Criando um type alias para o tipo legado

Após declarar o alias, é possível realizar o import do tipo LegacyCustomer em qualquer pacote do projeto e, assim, evitar confusões com o novo tipo Customer.

No exemplo a seguir podemos ver outra aplicação da criação de alias para referenciar a tipos. Observe o quão complicado é ter que ficar escrevendo o mesmo tipo repetidas vezes:

Método de exemplo de busca de restaurantes sem type alias

Neste caso, não podemos simplificar o nome com um import as, mas podemos utilizar um alias para tornar nosso código mais fácil de ler e escrever.

Método de exemplo de busca de restaurantes utilizando type alias

Outro exemplo de como tipos podem ser bastante complexos é na utilização do LiveData do Android. Recentemente eu me deparei com o seguinte código:

Exemplo de um tipo muito longo

Esse tipo, muito longo, pode ser simplificado utilizando alias, como no exemplo abaixo:

Utilizando typealias para simplificar um tipo

Observe que ele poderia ser ainda mais simplificado, mas vale reforçar que os alias devem ser utilizados de forma consciente, e não apenas adicionando um alias para todos os tipos genéricos que aparecerem no código.

Lembrando também que ao utilizar typealias nenhum tipo novo é criado. Em tempo de compilação o alias é substituído pelo seu tipo final. Assim, não há nenhum peso adicional no bytecode com o uso deles.

Inline class

Talvez você já tenha se deparado com um código como o a seguir:

Método de enviar evento sem especificar o tipo do parâmetro delay

Ao ver um código assim, sempre surge a dúvida de qual a escala utilizada no delay. Será que é em segundos, em milisegundos? Ou será que é em minutos? Mesmo utilizando um nome um pouco mais descritivo, ele serve apenas de lembrete e ainda podemos passar o valor errado por descuido.

Não seria muito legal se a gente declarasse o tipo do parâmetro de maneira que não haja erro?

Método de enviar evento com o tipo do parâmetro delay bem especificado

Sim! Só que criar uma classe para cada tipo de dado causa uma sobrecarga no acesso aos valores na memória e pode deixar o programa mais lento.

Foi para solucionar esse problema que as classes inline foram adicionadas no Kotlin na versão 1.3 (atualmente disponível apenas no modo experimental). Para utilizar, basta adicionar a palavra inline na declaração da classe:

Utilizando inline class para definir o tipo do parâmetro delay

Ao criar uma classe inline, você estará criando um novo tipo de dado, mas com a mesma performance de um tipo primitivo (na maioria dos casos). No exemplo acima, o código compilado será o mesmo de val delay = 10. Em tempo de execução, o Kotlin irá utilizar o tipo interno sempre que possível para melhorar a performance.

Por outro lado, as classes inline possuem algumas limitações. De modo geral, elas só podem ter apenas um property, inicializado no construtor padrão. Elas também não podem ter backing fields, mas podem declarar métodos. Além disso, elas não podem estender de nenhuma classe nem ser estendidas, mas podem implementar interfaces.

Mas então, qual a diferença entre as duas abordagens?

Ambas se assemelham muito, dado que ambas parecem não introduzir um novo tipo e serem representadas pelo seu tipo interno em tempo de execução. Entretanto, existe uma diferença muito importante entre as duas. Enquanto type aliases apenas criam um novo nome para referenciar um tipo, inline classes de fato criam um tipo novo que encapsula o tipo final (wrapper).

Com isso, há uma diferença fundamental na hora de tratar a tipagem das variáveis. Podemos, por exemplo, utilizar type aliases para tentar indicar quais os parâmetros corretos, mas o compilador não consegue entender que eles são tipos diferentes.

Utilizando typealias passando os parâmetros de forma errada

Podemos observar que nem o “novo tipo” do parâmetro (na verdade, o alias) ajuda a garantir que os parâmetros serão passados de forma correta. O compilador está verificando o tipo final do parâmetro, que nesse caso é String para os dois valores. Isso reforça o que foi dito anteriormente: ao utilizar type aliases nenhum tipo novo é criado.

Mas é possível garantir que os parâmetros serão passados na ordem correta utilizando inline classes. Caso os parâmetros não estejam sendo passados na ordem correta, o compilador vai indicar o erro em tempo de compilação.

Utilizando inline class passando os parâmetros de forma trocada

Neste caso, o compilador entende que o primeiro parâmetro deve ser, de fato, do tipo Username. Ao passar um objeto de outro tipo o erro é indicado em tempo de compilação, tornando o código menos propício a erros. E podemos ver, assim, que de fato um novo tipo está sendo criado com a utilização de inline class.

--

--