Anotações de Kotlin

Anotações de Kotlin

1. Visão geral

Neste tutorial, daremos uma visão geral das anotações do Kotlin.

Vamos demonstrar como aplicá-los, como criar e personalizar os nossos próprios. Em seguida, discutiremos brevemente a interação entre anotações e palavras-chave em Java e Kotlin.

Finalmente, daremos um exemplo simples de validação de classe que ilustra como podemos processar as anotações.

2. Aplicando anotações

No Kotlin, aplicamos uma anotação colocando seu nome prefixado com o símbolo @ na frente de um elemento de código. Por exemplo, se quisermos aplicar uma anotação chamadaPositive, devemos escrever o seguinte:

@Positive val amount: Float

Muitas vezes, as anotações têm parâmetros. The annotation parameters must be compile-time constants and must be of the following types:

  • Tipos primitivos Kotlin (Int, Byte, Short, Float, Double, Char,  Boolean)

  • enumerações

  • referências de classe

  • anotações

  • matrizes dos tipos mencionados acima

Se uma anotação exigir um parâmetro, forneceremos seu valor entre parênteses, como em uma chamada de função:

@SinceKotlin(version="1.3")

No caso, quando um parâmetro de anotação também é uma anotação, devemos omitir o símbolo @:

@Deprecated(message="Use rem(other) instead", replaceWith=ReplaceWith("rem(other)"))

Se um parâmetro de anotação for um objeto de classe, devemos adicionar::class ao nome da classe, por exemplo:

@Throws(IOException::class)

Se precisarmos especificar que um parâmetro de anotação pode ter vários valores, basta passar uma matriz desses valores:

@Throws(exceptionClasses=arrayOf(IOException::class, IllegalArgumentException::class))

A partir do Kotlin 1.2, também podemos usar a seguinte sintaxe:

@Throws(exceptionClasses=[IOException::class, IllegalArgumentException::class])

3. Declarações de anotações

Para declarar uma anotação, definimos uma classe e colocamos a palavra-chaveannotation antes declass one. Por sua natureza,declarations of annotation cannot contain any code.

A anotação mais simples não possui parâmetros:

annotation class Positive

A declaração de uma anotação que requer um parâmetro é como uma classe com um construtor primário:

annotation class Prefix(val prefix: String)

Quando declaramos nossas anotações personalizadas, devemos especificar a quais elementos de código eles podem ser aplicados e onde devem ser armazenados. As anotações usadas para definir esta meta-informação são chamadasmeta-annotations.

Nas próximas seções, vamos discuti-los brevemente. Para as informações mais recentes, podemos sempre verificar oofficial documentation.

3.1. @Target

Esta meta-anotação especifica a quais elementos de código essa anotação pode se referir. It has a required parameter that must be an instance of the AnnotationTarget enumeration or an array thereof. Portanto, podemos especificar que queremos aplicar nossa anotação aos seguintes elementos:

  • CLASSE

  • ANNOTATION_CLASS

  • TYPE_PARAMETER

  • PROPRIEDADE

  • CAMPO

  • LOCAL_VARIABLE

  • VALUE_PARAMETER

  • CONSTRUTOR

  • FUNÇÃO

  • PROPERTY_GETTER

  • PROPERTY_SETTER

  • TYPE

  • EXPRESSÃO

  • FILE

  • TYPEALIAS

Se não especificarmos explicitamente, a anotação correspondente poderá ser aplicada aos seguintes elementos por padrão:

CLASS, PROPERTY, FIELD, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER

3.2. @Retention

Esta meta-anotação especifica se a anotação deve ser armazenada no arquivo.class e se deve ser visível para uma reflexão. Its required parameter must be an instance of the AnnotationRetention enumeration que possui os seguintes elementos:

  • FONTE

  • BINÁRIO

  • TEMPO DE EXECUÇÃO

Ao contrário do Java, o valor padrão para@Retention em Kotlin éRUNTIME.

3.3. @Repeatable

@Repeatable especifica se um elemento pode ter várias anotações do mesmo tipo. Esta meta-anotação não aceita parâmetros.

3.4. @MustBeDocumented

@MustBeDocumented especifica se a documentação da anotação deve ser incluída na documentação gerada. Essa meta-anotação também não aceita parâmetros.

4. Interoperabilidade Java com anotações

Kotlin é geralmente mais conciso em relação ao Java. Por exemplo, ele cria automaticamente métodos adicionais para nós, como quando declaramos uma propriedade:

val name: String?;

o compilador cria automaticamente um campo privado e um getter e setter para essa propriedade. Como resultado, surge uma pergunta: se adicionarmos uma anotação à propriedade, onde ela será aplicada? Para o getter, setter ou para o próprio campo?

Em Kotlin, se decorarmos uma propriedade com uma anotação definida em um código Java, ela é aplicada ao campo correspondente.

Agora podemos enfrentar um problema se a anotação requer que um campo seja público, por exemplo, com a anotação@Rule de JUnit. Para evitar ambiguidade, o Kotlin possui a chamada declaração de destino do site de uso.

4.1. Declarações de destino do site de uso

Os destinos do site de uso são opcionais. Nós os colocamos entre o símbolo@ e o nome da anotação, usando o sinal de dois pontos como separador. A sintaxe nos permite especificar vários nomes de anotação ao mesmo tempo:

image

No caso de colocar@get:Positive em um campo Kotlin, isso significaria que a anotação deveria realmente ter como alvo o getter gerado para aquele campo.

O Kotlin suporta os seguintes valores dos destinos do site de uso que correspondem a:

  • delegate - um campo que armazena uma propriedade delegada

  • field - um campo gerado para uma propriedade

  • file - uma classe que contém funções de nível superior e propriedades definidas naquele arquivo

  • get/set - a propriedade getter / setter

  • param - um parâmetro do construtor

  • property - a propriedade do Kotlin, não é acessível a partir do código Java

  • receiver - o parâmetro receptor de uma função de extensão ou propriedade

As seguintes anotações do Kotlin permitem personalizar como elas podem ser usadas no código Java:

  • @JvmName - permite alterar o nome do método ou campo Java gerado

  • @JvmStatic - permite-nos especificar que o método ou campo Java gerado deve ser estático

  • @JvmOverloads - indica que o compilador Kotlin deve gerar funções de sobrecarga substituindo os parâmetros padrão

  • @JvmField - indica que o campo Java gerado deve ser público sem getter / setter

Algumas anotações Java tornam-se palavras-chave do Kotlin e vice-versa:

Java Kotlin

@Sobrepor

sobrepor

volátil

@Volátil

strictfp

@Strictfp

sincronizado

@synchronized

transitório

@Transient

joga

@Throws

5. Anotações de processamento

Para demonstrar como podemos processar anotações, vamos criar um validador simples. Aqui apresentamos apenas a ideia enquanto o código completo está disponível em nossorepository no Github.

Suponha que devamos decidir se uma instância deItem  é válida:

class Item(val amount: Float, val name: String)

Assumimos que uma instânciaItem é válida se o valor deamount for positivo e o valor dename forAlice ouBob.

Para este fim, vamos decorar as propriedades da classeItem com nossas anotações personalizadas:PositiveeAllowedNames:

class Item(
  @Positive val amount: Float,
  @AllowedNames(["Alice", "Bob"]) val name: String)

Em nossa implementação ingênua, apenas obtemos as propriedades deItem:

val fields = item::class.java.declaredFields
for (field in fields) {...}

e itere sobre todas as anotações de cada propriedade:

for (annotation in field.annotations) {...}

para encontrar aqueles com os quais decoramos as propriedades deItem.

Por exemplo, podemos detectar seAllowedNames está presente em uma propriedade por meio do comando:

field.isAnnotationPresent(AllowedNames::class.java)

Depois que a anotação estiver presente, poderemos decidir facilmente se a propriedade tem um valor válido apenas comparando-o com os valores permitidos:

val allowedNames = field.getAnnotation(AllowedNames::class.java)?.names

We should be aware that annotations use the Java Reflection API. Como resultado, o desempenho do código pode sofrer se dependermos fortemente de anotações.

6. Conclusão

Neste artigo, consideramos as anotações Kotlin e suas contrapartes Java. Descrevemos como aplicar anotações Kotlin, como criar anotações personalizadas e, em seguida, como processá-las.

Como podemos ver, as anotações do Kotlin são bastante semelhantes às do Java. No entanto, nosso tutorialCreating a Custom Annotation in Java também pode ser útil.

Como sempre, o código completo está disponível emin our GitHub repository.