Escolar Documentos
Profissional Documentos
Cultura Documentos
Declaração de variável
Use val para uma variável cujo valor nunca muda. Não é possível reatribuir um
valor a uma variável que tenha sido declarada usando val.
Use var para uma variável cujo valor possa ser mudado.
Int é um tipo que representa um número inteiro, um dos muitos tipos numéricos que
podem ser representados em Kotlin. Assim como acontece com outras linguagens,
você também pode usar Byte, Short, Long, Float e Double, dependendo dos seus
dados numéricos.
Essas palavras-chave permitem que você seja explícito sobre o que pode ser mudado.
Use-as em seu favor conforme necessário. Se uma referência de variável precisar ser
reatribuível, declare-a como var. Do contrário, use val.
Inferência de tipo
1
No exemplo a seguir, languageName é inferido como String. Portanto, não é possível
chamar nenhuma função que não faça parte da classe String:
// Fails to compile
languageName.inc()
Segurança nula
Em algumas linguagens, uma variável de tipo de referência pode ser declarada sem
fornecer um valor explícito inicial. Nesses casos, as variáveis geralmente contêm um
valor nulo. Por padrão, as variáveis do Kotlin não podem reter valores nulos. Isso
significa que o snippet a seguir é inválido.
// Fails to compile
val languageName: String = null
Para que uma variável mantenha um valor nulo, ela precisa ser do tipo anulável. Você
pode especificar uma variável como sendo anulável, usando um sufixo do tipo com ?,
conforme mostrado neste exemplo a seguir.
Você precisa lidar com variáveis anuláveis com cuidado ou corre o risco de ter
um NullPointerException. Em Java, por exemplo, se você tentar invocar um método
em um valor nulo, seu programa falhará.
O Kotlin fornece uma série de mecanismos para trabalhar com segurança com
variáveis anuláveis. Para ver mais informações, consulte Padrões comuns do Kotlin no
Android: anulação (link em inglês).
Condicionais
2
if (count == 42) {
println("I have the answer.")
} else {
println("The answer eludes me.")
}
Você pode representar várias condições usando else if. Isso permite representar uma
lógica mais granular e complexa em uma única instrução condicional, conforme
mostrado neste exemplo:
if (count == 42) {
println("I have the answer.")
} else if (count > 35) {
println("The answer is close.")
} else {
println("The answer eludes me.")
}
As instruções condicionais são úteis para representar a lógica com estado, mas você
pode se repetir ao gravá-las. No exemplo acima, você simplesmente imprime
um String em cada ramificação. Para evitar essa repetição, o Kotlin
oferece expressões condicionais. O último exemplo pode ser regravado da seguinte
forma:
println(answerString)
3
val answerString = when {
count == 42 -> "I have the answer."
count > 35 -> "The answer is close."
else -> "The answer eludes me."
}
println(answerString)
Funções
Com base nos exemplos anteriores, veja uma função completa do Kotlin:
4
} else {
"The answer eludes me"
}
return answerString
}
return answerString
}
5
fun generateAnswerString(countThreshold: Int): String {
return if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
}
Funções anônimas
Nem todas as funções precisam de um nome. Algumas funções são identificadas mais
diretamente por suas entradas e saídas. Essas funções são chamadas de funções
anônimas. Você pode manter uma referência a uma função anônima usando essa
referência para chamar a função anônima posteriormente. Você também pode passar
a referência no seu app, como acontece com outros tipos de referência.
6
Uma função pode usar outra função como um argumento. As funções que usam outras
funções como argumentos são chamadas de funções de ordem superior. Esse padrão
é útil para a comunicação entre componentes, da mesma forma que você pode usar
uma interface de callback em Java.
Classes
class Car
Propriedades
7
backup. Como um carro precisa de rodas para dirigir, você pode adicionar uma lista de
objetos Wheel como uma propriedade de Car, conforme mostrado neste exemplo:
class Car {
val wheels = listOf<Wheel>()
}
8
propriedade ao restringir o acesso a setter, você poderá designar esse setter
como private:
Com uma combinação de propriedades e funções, você pode criar classes que
modelam todos os tipos de objeto.
Interoperabilidade
A seguir
Este tópico se concentra em alguns dos aspectos mais úteis da linguagem Kotlin ao
desenvolver para o Android.
9
Herança
Dentro de LoginFragment, você pode modificar vários callbacks de ciclo de vida para
responder a mudanças de estado no Fragment. Para substituir uma função, use a
palavra-chave override, conforme mostrado no exemplo a seguir:
Nulidade e inicialização
Nos exemplos anteriores, alguns dos parâmetros nos métodos modificados têm tipos
sufixados com um ponto de interrogação ?. Isso indica que os argumentos passados
para esses parâmetros podem ser nulos. Não deixe de usar a proteção contra valores
nulos (link em inglês).
10
class LoginFragment : Fragment() {
usernameEditText = view.findViewById(R.id.username_edit_text)
passwordEditText = view.findViewById(R.id.password_edit_text)
loginButton = view.findViewById(R.id.login_button)
statusTextView = view.findViewById(R.id.status_text_view)
}
...
}
Observação: se você acessar uma propriedade antes que ela seja inicializada, o
Kotlin lançará um UninitializedPropertyAccessException.
Conversão de SAM
loginButton.setOnClickListener {
val authSuccessful: Boolean = viewModel.authenticate(
usernameEditText.text.toString(),
passwordEditText.text.toString()
)
if (authSuccessful) {
// Navigate to next screen
} else {
statusTextView.text = requireContext().getString(R.string.auth_failed)
}
11
}
Objetos complementares
...
companion object {
private const val TAG = "LoginFragment"
}
}
Você pode definir TAG no nível superior do arquivo, mas ele também pode ter um
grande número de variáveis, funções e classes que também são definidas no nível
superior. Os objetos complementares ajudam a conectar variáveis, funções e a
definição de classe sem fazer referência a nenhuma instância específica dessa classe.
Delegação de propriedade
Ao inicializar propriedades, você pode repetir alguns dos padrões mais comuns do
Android, como acessar um ViewModel em um Fragment. Para evitar excesso de
código duplicado, você pode usar a sintaxe de delegação de propriedade do Kotlin.
Nulidade
12
O Kotlin fornece regras de nulidade rigorosas que mantêm a segurança de tipo em
todo o app. No Kotlin, as referências a objetos não podem conter valores nulos por
padrão. Para atribuir um valor nulo a uma variável, você precisa declarar um tipo de
variável anulável adicionando ? ao final do tipo base.
Interoperabilidade
As regras rígidas do Kotlin tornam seu código mais seguro e conciso. Essas regras
diminuem as chances de haver um NullPointerException que cause falhas no app.
Além disso, elas reduzem o número de verificações de nulos que você precisa fazer
no código.
Muitas vezes, você também precisa chamar um código que não é do Kotlin ao criar um
app para Android, porque a maioria das APIs do Android é criada na linguagem de
programação Java.
Tipos de plataforma
Para resolver esse problema, use anotações de nulidade sempre que escrever um
código em Java. Essas anotações ajudam desenvolvedores Java e Kotlin.
13
Por exemplo, veja a classe Account como ela é definida em Java:
...
}
Para indicar que uma variável nunca pode ser nula, use a anotação @NonNull:
Se não tiver certeza sobre um tipo de Java, considere que ele é anulável. Como
exemplo, o membro name da classe Account não está anotado, então você pode
presumir que ele é String anulável.
O operador !! trata tudo no lado esquerdo como não nulo, portanto, neste caso, você
está tratando name como um String não nulo. Se o resultado da expressão à esquerda
for nulo, seu app lançará um NullPointerException. Esse operador é rápido e fácil, mas
é preciso usá-lo com moderação, porque ele pode reintroduzir instâncias
de NullPointerException no seu código.
14
val account = Account("name", "type")
val accountName = account.name?.trim()
Você também pode usar o operador Elvis para retornar de uma função
antecipadamente, como mostrado no exemplo a seguir:
...
}
As APIs do Android estão se tornando cada vez mais compatíveis com o Kotlin. Muitas
das APIs mais comuns do Android, incluindo AppCompatActivity e Fragment, contêm
anotações de nulidade, e algumas chamadas como Fragment#getContext têm mais
alternativas otimizadas para Kotlin.
15
acessar as propriedades e funções. Para alguns desses cenários, o Android contém
APIs alternativas que oferecem essa conveniência. Fragment#requireContext, por
exemplo, retorna um Context não nulo e lança um IllegalStateException se chamado
quando um Context seria nulo. Dessa forma, é possível tratar o Context resultante
como não nulo sem a necessidade de operadores safe-call ou soluções alternativas.
Inicialização de propriedades
Propriedades em Kotlin não são inicializadas por padrão. Elas precisam ser
inicializadas quando a classe envolvida for inicializada.
init {
index = 12
}
}
No entanto, você pode ter algumas propriedades que não podem ser inicializadas
durante a construção do objeto. Por exemplo, talvez você queira referenciar um View a
partir de um Fragment, o que significa que o layout precisa ser inflado primeiro. A
inflação não ocorre quando um Fragment é construído. Em vez disso, ele é inflado ao
chamar Fragment#onCreateView.
Uma maneira de lidar com esse cenário é declarar a visualização como anulável e
inicializá-la o mais rápido possível, conforme mostrado no exemplo a seguir:
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView?.setText(R.string.auth_failed)
}
16
}
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView.setText(R.string.auth_failed)
}
}
Arquivos de origem
Nomeação
17
Se um arquivo de origem contiver somente uma única classe de nível superior, o nome
do arquivo precisará refletir o nome que diferencia maiúsculas de minúsculas e a
extensão .kt. Caso contrário, se um arquivo de origem contiver várias declarações de
nível superior, escolha um nome que descreva o conteúdo do arquivo, aplique o
PascalCase e anexe a extensão .kt.
// MyClass.kt
class MyClass { }
// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …
Caracteres especiais
Para os caracteres não ASCII restantes, será usado o caractere Unicode real (por
exemplo, ∞) ou o escape Unicode equivalente (por exemplo, \u221e). Será escolhido
aquele que torna o código mais fácil de ler e entender. Escapes Unicode não são
recomendados para caracteres de impressão em nenhum local e fora de literais de
string e comentários.
18
Exemplo Discussão
val unitAbbrev = Ruim: não há motivo para usar um escape com um caractere de
"\u03bcs" // μs impressão.
val unitAbbrev = "\u03bcs"` Ruim: o leitor não tem ideia do que seja isso.
return "\ufeff" + content Bom: use escapes para caracteres não imprimíveis e comente,
se necessário.
Estrutura
Direitos autorais/licença
/*
* Copyright 2017 Google, Inc.
*
* ...
*/
/**
* Copyright 2017 Google, Inc.
*
* ...
19
*/
Declaração do pacote
Declarações de importação
Os arquivos de origem geralmente são lidos de cima para baixo, o que significa que a
ordem, em geral, precisa refletir que as declarações mais acima informarão a
compreensão das que estão mais abaixo. Arquivos diferentes podem optar por
ordenar os conteúdos de forma diferente. Da mesma forma, um arquivo pode conter
100 propriedades, outras 10 funções e ainda uma única classe.
20
habitualmente ao fim da classe, já que resultariam em ordenação "cronológica por
data de adição", o que não é uma ordem lógica.
A ordem dos membros em uma classe segue as mesmas regras das declarações de
nível superior.
Formatação
Chaves
if (string.isEmpty()) return
when (value) {
0 -> return
// …
}
if (string.isEmpty())
return // WRONG!
if (string.isEmpty()) {
return // Okay
}
21
return Runnable {
while (condition()) {
foo()
}
}
Blocos vazios
Um bloco vazio ou uma construção parecida com um bloco precisa estar no estilo
K&R.
try {
doSomething()
} catch (e: Exception) {} // WRONG!
try {
doSomething()
} catch (e: Exception) {
} // Okay
22
Expressões
Recuo
Cada declaração é seguida por uma quebra de linha. Pontos e vírgulas não são
usados.
Quebra de linha
Exceções:
Não é possível usar linhas que obedecem ao limite de colunas (por exemplo,
um URL longo no KDoc)
Declarações package e import
Linhas de comando em um comentário que pode ser recortado e colado em um
shell
23
Onde quebrar
Funções
Quando uma assinatura de função não se encaixar em uma única linha, divida cada
declaração de parâmetro na própria linha. Os parâmetros definidos nesse formato
precisam usar um único recuo (+4). O parêntese de fechamento ()) e o tipo de retorno
são colocados na própria linha sem recuo adicional.
Funções de expressão
Quando uma função contém apenas uma única expressão, ela pode ser representada
como uma função de expressão (link em inglês).
24
override fun toString(): String = "Hey"
O único momento em que uma função de expressão precisa se ajustar a várias linhas
é quando ela abre um bloco.
Caso contrário, se uma função de expressão aumentar e exigir quebra, use um corpo
de função normal, uma declaração return e regras normais de quebra de expressão.
Propriedades
As propriedades somente leitura podem usar uma sintaxe mais curta que caiba em
uma única linha.
Espaço em branco
Vertical
25
Exceção: uma linha em branco entre duas propriedades consecutivas
(não tendo outro código entre elas) é opcional. Essas linhas em branco são usadas
conforme necessário para criar agrupamentos lógicos de propriedades e associar
propriedades com a propriedade de backup, se presente.
Exceção: linhas em branco entre constantes de enumeração são
abordadas abaixo.
Entre declarações, conforme necessário, para organizar o código em
subseções lógicas.
Opcionalmente antes da primeira declaração em uma função, antes do primeiro
membro de uma classe ou depois do último membro de uma classe (não incentivado
nem desencorajado).
Conforme exigido por outras seções deste documento (como a
seção Estrutura).
Várias linhas em branco consecutivas são permitidas, mas não são recomendadas
nem necessárias.
Horizontal
Além de onde exigido pela linguagem ou outras regras de estilo, além de literais,
comentários e KDoc, um único espaço ASCII também aparece somente nos seguintes
locais:
// WRONG!
for(i in 0..1) {
}
// Okay
for (i in 0..1) {
}
// WRONG!
}else {
}
// Okay
} else {
}
26
// WRONG!
if (list.isEmpty()){
}
// Okay
if (list.isEmpty()) {
}
// WRONG!
val two = 1+1
// Okay
val two = 1 + 1
// WRONG!
ints.map { value->value.toString() }
// Okay
ints.map { value -> value.toString() }
Mas não:
// WRONG!
val toString = Any :: toString
// Okay
val toString = Any::toString
// WRONG
it . toString()
// Okay
it.toString()
27
// WRONG
for (i in 1 .. 4) print(i)
// Okay
for (i in 1..4) print(i)
Antes de dois pontos (:) somente se usado em uma declaração de classe para
especificar uma classe base ou interfaces, ou quando usado em uma
cláusula where para restrições genéricas (link em inglês).
// WRONG!
class Foo: Runnable
// Okay
class Foo : Runnable
// WRONG
fun <T: Comparable> max(a: T, b: T)
// Okay
fun <T : Comparable> max(a: T, b: T)
// WRONG
fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay
fun <T> max(a: T, b: T) where T : Comparable<T>
// WRONG!
val oneAndTwo = listOf(1,2)
// Okay
val oneAndTwo = listOf(1, 2)
// WRONG!
class Foo :Runnable
// Okay
class Foo : Runnable
28
// WRONG!
var debugging = false//disabled by default
// Okay
var debugging = false // disabled by default
Construções específicas
Classes de enumeração
MAYBE {
override fun toString() = """¯\_(ツ)_/¯"""
}
}
Como as classes de enumeração são classes, todas as outras regras para classes de
formatação se aplicam.
Anotações
@Retention(SOURCE)
@Target(FUNCTION, PROPERTY_SETTER, FIELD)
annotation class Global
29
Anotações sem argumentos podem ser colocadas em uma única linha.
@JvmField @Volatile
var disposable: Disposable? = null
Quando uma única anotação sem argumentos estiver presente, ela poderá ser
colocada na mesma linha que a declaração.
A sintaxe @[...] só pode ser usada com um destino de site de uso explícito e somente
para combinar duas ou mais anotações sem argumentos em uma única linha.
@field:[JvmStatic Volatile]
var disposable: Disposable? = null
Ao gravar uma biblioteca, retenha a declaração de tipo explícito quando ela fizer parte
da API public.
Nomeação
30
Prefixos ou sufixos especiais, como os exibidos nos
exemplos name_, mName, s_name e kName, não são usados, exceto no caso de
propriedades de backup (consulte Propriedades de backup).
Nomes de pacote
// Okay
package com.example.deepspace
// WRONG!
package com.example.deepSpace
// WRONG!
package com.example.deep_space
Nomes de tipo
As classes de teste são nomeadas começando com o nome da classe que estão
testando e terminando com Test. Por exemplo, HashTest ou HashIntegrationTest.
Nomes de função
Nomes das funções são escritos em camelCase e normalmente são verbos ou frases
com verbo. Por exemplo, sendMessage ou stop.
Nomes de constante
31
Constantes são propriedades val sem nenhuma função get personalizada, cujos
conteúdos são imutáveis e cujas funções não têm efeitos colaterais detectáveis. Isso
inclui tipos imutáveis e coleções imutáveis de tipos imutáveis, bem como escalares e
strings, se marcados como const. Se algum estado observável da instância puder ser
modificado, ele não será uma constante. Apenas tentar não mudar o objeto não é
suficiente.
Propriedades de backup
32
get() {
if (_table == null) {
_table = HashMap()
}
return _table ?: throw AssertionError()
}
Letras concatenadas
33
Forma de prosa Correta Incorreta
Documentação
Formatação
/**
* Multiple lines of KDoc text are written here,
* wrapped normally…
*/
fun method(arg: String) {
// …
}
A forma básica é sempre aceitável. A forma de uma única linha pode ser substituída
quando a totalidade do bloco KDoc (incluindo marcadores de comentário) couber em
uma única linha. Isso só se aplica quando não há tags de bloco, como @return.
34
Parágrafos
Uma linha em branco, ou seja, uma linha contendo apenas o asterisco inicial alinhado
(*), aparece entre os parágrafos e antes do grupo de tags de bloco, se houver.
Tags de bloco
Qualquer uma das "tags de bloco" padrão que são usadas aparece na
ordem @constructor, @receiver, @param, @property, @return, @throws, @see e
elas nunca aparecem com uma descrição vazia. Quando uma tag de bloco não se
encaixa em uma única linha, as linhas de continuação são recuadas 4 espaços a partir
da posição de @.
Fragmento de resumo
Cada bloco KDoc começa com um breve fragmento de resumo. Esse fragmento é
muito importante: é a única parte do texto que aparece em determinados contextos,
como índices de classe e método.
Isso é um fragmento: uma frase nominal ou uma frase verbal, não uma frase completa.
Não começa com "A `Foo` is a..." ou "This method returns...", nem tem que formar
uma frase imperativa completa como "Save the record.". No entanto, o fragmento é
capitalizado e pontuado como se fosse uma frase completa.
Uso
Exceção: substituições
35