Olá Habr! Trago à sua atenção a tradução do autor da página de documentação das Convenções de codificação Kotlin do JetBrains.
→ Documentação original
Conteúdo:
Usando o guia de estilo em Intellij Idea
Para aplicar a formatação no Intellij Idea de acordo com o manual atual, você precisa instalar o plugin Kotlin versão 1.2.20 ou posterior, vá para Configurações | Editor Estilo de código | Kotlin, clique no link "Definir de ..." no canto superior direito e selecione "Estilo predefinido" / Guia de estilo Kotlin "no menu suspenso.
Para verificar se o seu código está formatado de acordo com o estilo recomendado, vá para a configuração de inspeção e ative a opção "Kotlin | Problemas de estilo | O arquivo não está formatado de acordo com as configurações do projeto". Outras regras de validação, como convenções de nomenclatura, são ativadas por padrão.
Estrutura do projeto
Estrutura de pastas
Em projetos que usam idiomas diferentes, os arquivos com código Kotlin devem estar na mesma pasta que o código em outros idiomas e usar a mesma estrutura de arquivo aceita para o idioma principal. Por exemplo, para Java, os arquivos devem estar na estrutura da pasta de acordo com o nome do pacote.
Em projetos que usam apenas o Kotlin, a estrutura de pastas recomendada é: use pastas para organizar pacotes com o diretório raiz ignorado, ou seja, se todo o código do projeto estiver no pacote "org.example.kotlin" e seus pacotes, os arquivos de origem pertencentes ao pacote "org.example.kotlin" deverão estar no diretório raiz do projeto e os arquivos com o código-fonte do pacote "org. example.kotlin.foo.bar "deve estar no subdiretório" foo / bar "em relação à raiz do projeto.
Nome dos arquivos de origem
Se o arquivo Kotlin contiver apenas uma classe (possivelmente relacionada à declaração de nível superior), deverá ser nomeado, assim como uma classe com a extensão .kt
. Se o arquivo contiver várias classes ou possuir apenas declarações de nível superior, escolha um nome que descreva o que o arquivo contém e nomeie o arquivo de acordo. Use camel hump com uma primeira letra maiúscula para nomear arquivos (por exemplo, ProcessDeclarations.kt
).
O nome do arquivo deve descrever o que o código faz no arquivo. Ou seja, você deve evitar palavras sem sentido como "Util" para nomear arquivos.
Organizando arquivos de origem
A colocação de várias declarações (classes, funções ou propriedades de nível superior) no mesmo arquivo de origem Kotlin é bem-vinda se essas declarações estiverem intimamente relacionadas entre si semântica e o tamanho do arquivo permanecer razoável (não mais do que algumas centenas de linhas).
Em particular, ao definir funções de extensão para uma classe que se apliquem a todos os aspectos da aplicação dessa classe, coloque-as no mesmo arquivo em que a própria classe está definida. Ao definir funções de extensão significativas apenas para o contexto específico de uso dessa classe, coloque-as ao lado do código que usa a função de extensão. Não crie arquivos apenas para armazenar "todas as extensões Foo".
Estrutura de classe
Normalmente, o conteúdo de uma classe é classificado na seguinte ordem:
- Declarações de propriedade e blocos de inicializador
- Construtores secundários
- Declarações de método
- Objetos complementares
Não classifique as declarações de método em ordem alfabética ou visual e não separe os métodos comuns dos métodos de extensão. Em vez disso, monte o código conectado logicamente para que alguém que leia a classe de cima para baixo possa seguir a lógica do que acontece. Escolha uma ordem de classificação (código de nível superior primeiro [itens de nível superior primeiro] e detalhes posteriormente ou vice-versa) e cumpra-os.
Coloque as classes aninhadas ao lado do código que usa essas classes. Se as classes são destinadas ao uso externo e não são referenciadas dentro da classe, coloque-as no final após o objeto complementar.
Estrutura de Implementação de Interface
Ao implementar uma interface, mantenha a mesma estrutura que a interface que está sendo implementada (se necessário, alternando-a com métodos particulares adicionais usados para implementação)
Substitui a estrutura
Redefinições sempre juntas, uma após a outra.
Regras de nomeação
Kotlin segue as mesmas convenções de nomenclatura que Java. Em particular:
Nomes de pacotes em minúsculas e não usam sublinhados (org.example.myproject). Geralmente, não é recomendado o uso de nomes de várias palavras, mas se você precisar usar várias palavras, basta combiná-las ou usar camel hump (org.examle.myProject).
Os nomes em maiúscula e o objeto começam com uma letra maiúscula e usam camel hump:
open class DeclarationProcessor { ... } object EmptyDeclarationProcessor : DeclarationProcessor() { ... }
Nome da Função
Os nomes de funções, propriedades e variáveis locais começam com uma letra minúscula e não contêm sublinhados:
fun processDeclarations() { ... } var declarationCount = ...
Exceção: as funções de fábrica usadas para instanciar classes podem ter o mesmo nome que a classe que está sendo criada:
abstract class Foo { ... } class FooImpl : Foo { ... } fun Foo(): Foo { return FooImpl(...) }
Nome dos métodos de teste
Nos testes (e somente nos testes), é permitido usar nomes de métodos com espaços entre vírgulas invertidas. (Observe que esses nomes de métodos atualmente não são suportados pelo tempo de execução do Android.) Sublinhados nos nomes de métodos também são permitidos no código de teste.
class MyTestCase { @Test fun `ensure everything works`() { ... } @Test fun ensureEverythingWorks_onAndroid() { ... } }
Nomeação de propriedade
Os nomes constantes (propriedades rotuladas const
ou propriedades de nível superior ou um objeto val
sem uma função get
personalizada que contém dados imutáveis) devem ser capitalizados, separados por sublinhados:
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
Os nomes de nível superior ou propriedades de objetos que contêm objetos com comportamento ou dados mutáveis devem usar nomes comuns no camel hump:
val mutableCollection: MutableSet<String> = HashSet()
Os nomes de propriedades que fazem referência a objetos Singleton podem usar o mesmo estilo de nomeação que as declarações de classe:
val PersonComparator: Comparator<Person> = ...
Para enumerações, você pode usar nomes escritos em letras maiúsculas separadas por sublinhados ou no estilo camel hump, começando com uma letra maiúscula, dependendo do uso.
enum class Color { RED, GREEN }
enum class Color { RedColor, GreenColor }
Nota do tradutor: apenas não misture estilos diferentes. Escolha um estilo e siga-o no seu design.
Nomeando propriedades ocultas
Se uma classe tiver duas propriedades que são conceitualmente iguais, mas uma faz parte da API pública e a outra faz parte da implementação, use o sublinhado como um prefixo para o nome da propriedade oculta:
class C { private val _elementList = mutableListOf<Element>() val elementList: List<Element> get() = _elementList }
Escolhendo os nomes certos
O nome da classe geralmente é um substantivo ou frase que explica o que é a classe:
List, PersonReader
O nome do método geralmente é uma ação de verbo ou frase que explica o que o método faz:
close, readPersons
O nome também deve indicar se o método altera o objeto ou retorna um novo. Por exemplo, sort
é uma ordenação que altera a coleção e sorted
é o retorno de uma nova cópia ordenada da coleção.
Os nomes devem indicar claramente o objetivo da entidade; portanto, é melhor evitar o uso de palavras sem sentido ( Manager
, Wrapper
etc.) nos nomes.
Ao usar o acrônimo como parte do nome do anúncio, use letras maiúsculas se ele consistir em duas letras ( IOStream
); ou maiúscula apenas a primeira letra, se for mais longa ( XmlFormatter
, HttpInputStream
).
Na maioria dos casos, o Kotlin segue as convenções de formatação Java.
Use 4 espaços para recuar. Não use guias.
Para chaves, coloque a chave de abertura no final da linha onde a estrutura começa e a chave de fechamento em uma linha separada, alinhada horizontalmente com a estrutura de abertura.
if (elements != null) { for (element in elements) {
(Nota: no Kotlin, um ponto-e-vírgula é opcional, portanto, o agrupamento de linhas é importante. O design da linguagem envolve chaves no estilo Java, e você pode encontrar um comportamento inesperado de execução de código se tentar usar um estilo de formatação diferente.)
Espaços horizontais
Coloque espaços ao redor de operadores binários (a + b)
. Exceção: não coloque espaços ao redor do operador "range to" (0..i)
Não coloque espaços ao redor de operadores unários (a++)
Coloque espaços entre as palavras-chave de controle ( if
, when
, for
e while
) e os colchetes de abertura correspondentes.
Não coloque um espaço antes do colchete de abertura na declaração principal de um construtor, método ou método.
class A(val x: Int) fun foo(x: Int) { ... } fun bar() { foo(1) }
Nunca coloque um espaço depois de (
, [
ou antes ]
, )
.
Nunca coloque espaço em torno de um ponto .
ou operador ?.
:
foo.bar().filter { it > 2 }.joinToString() foo?.()
Coloque um espaço após a barra dupla para comentar //
:
Não coloque espaços ao redor dos colchetes angulares usados para indicar parâmetros de tipo:
Class Map<K, V> { ... }
Não coloque espaços ao redor dos dois pontos duplos para indicar uma referência ao método ::
class:
Foo::class String::length
Não coloque um espaço antes ?
usado para marcar um null
:
String?
Geralmente, evite qualquer tipo de alinhamento horizontal. Renomear um identificador para um nome de tamanho diferente não deve afetar a formatação do código.
Cólon
Coloque um espaço antes dos dois pontos :
nos seguintes casos:
- quando é usado para separar o tipo do supertipo;
abstract class Foo<out T : Any>
- ao delegar para um construtor de superclasse ou outro construtor da mesma classe;
constructor(x: String) : super(x) { ... } constructor(x: String) : this(x) { ... }
- após o objeto de palavra-chave.
val x = object : IFoo { ... }
Não coloque um espaço antes :
quando ele separa um anúncio e seu tipo.
abstract fun foo(a: Int): T
Coloque sempre um espaço após :
abstract class Foo<out T : Any> : IFoo { abstract fun foo(a: Int): T } class FooImpl : Foo() { constructor(x: String) : this(x) { ... } val x = object : IFoo { ... } }
Classes com vários parâmetros básicos do construtor e nomes abreviados podem ser escritas em uma linha:
class Person(id: Int, name: String)
Classes com nomes mais longos ou o número de parâmetros devem ser formatadas para que cada parâmetro principal do construtor esteja em uma linha separada com recuo. Além disso, o suporte de fechamento deve estar em uma nova linha. Se usarmos herança, a chamada para o construtor da superclasse ou a lista de interfaces implementadas deve estar localizada na mesma linha que o colchete:
class Person( id: Int, name: String, surname: String ) : Human(id, name) { ... }
Ao especificar a interface e chamar o construtor da superclasse, o construtor da superclasse deve primeiro ser localizado e, em seguida, o nome da interface em uma nova linha é justificado à esquerda:
class Person( id: Int, name: String, surname: String ) : Human(id, name), KotlinMaker { ... }
Para classes com uma longa lista de supertipos, é necessário colocar uma quebra de linha após os dois pontos e alinhar todos os nomes de supertipos horizontalmente à esquerda:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
Para separar claramente o cabeçalho da classe e seu corpo quando o cabeçalho da classe for longo, coloque uma linha vazia após o cabeçalho da classe (como no exemplo acima) ou coloque a chave de abertura em uma linha separada:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
Use recuo regular (4 espaços) para parâmetros do construtor.
Justificativa: isso garante que as propriedades declaradas no construtor principal tenham o mesmo recuo que as propriedades declaradas no corpo da classe.
Modificadores
Se um anúncio contiver vários modificadores, organize-os sempre na seguinte ordem:
public / protected / private / internal expect / actual final / open / abstract / sealed / const external override lateinit tailrec vararg suspend inner enum / annotation companion inline infix operator data
Coloque todas as anotações antes dos modificadores:
@Named("Foo") private val foo: Foo
Se você não estiver trabalhando em uma biblioteca, omita modificadores redundantes (por exemplo, público).
As anotações geralmente são colocadas em linhas separadas antes da declaração à qual estão anexadas e com o mesmo travessão:
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
As anotações sem argumentos podem ser localizadas em uma linha:
@JsonExclude @JvmField var x: String
Uma anotação sem argumentos pode ser colocada na mesma linha da declaração correspondente:
@Test fun foo() { ... }
Anotações de arquivo
As anotações nos arquivos são colocadas após o comentário no arquivo (se houver), antes da instrução do pacote e são separadas do pacote por uma linha vazia (para enfatizar o fato de que elas são direcionadas ao arquivo, não ao pacote).
@file:JvmName("FooBar") package foo.bar
Se a assinatura do método não couber em uma linha, use a seguinte sintaxe:
fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType ): ReturnType {
Use recuo regular (4 espaços) para parâmetros de função.
Justificativa: consistência com os parâmetros do construtor
É preferível usar uma expressão sem chaves para funções que consistem em uma linha.
fun foo(): Int {
Se o corpo de uma função de linha única não couber na mesma linha que a declaração, coloque o sinal = na primeira linha. Recue o corpo da expressão em 4 espaços.
fun f(x: String) = x.length
Para propriedades simples de somente leitura, é preferível usar a formatação de linha única:
val isEmpty: Boolean get() = size == 0
Para propriedades mais complexas, sempre use get
e set
em linhas separadas:
val foo: String get() { ... }
Para propriedades com inicialização, se o inicializador for muito longo, adicione uma quebra de linha após o sinal de igual e um recuo de quatro espaços para a cadeia de inicialização:
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
Se a condição na instrução de controle if
ou when
for multilinhas, sempre use chaves em volta do corpo da instrução. Recue cada linha subsequente da condição por 4 espaços em relação ao início da instrução. Coloque os colchetes da condição junto com a chave de abertura em uma linha separada:
if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) ) { return createKotlinNotConfiguredPanel(module) }
Justificação: alinhamento limpo e separação clara do corpo e do corpo da condição
Coloque o else
, catch
, finally
palavras-chave, bem como a palavra-chave while do loop do / while na mesma linha do colchete de fechamento anterior:
if (condition) {
Se as condições when
das instruções consistirem em vários blocos, é recomendável separá-las uma da outra com uma linha vazia:
private fun parsePropertyValue(propName: String, token: Token) { when (token) { is Token.ValueToken -> callback.visitValue(propName, token.value) Token.LBRACE -> {
Coloque os blocos curtos de instruções when
na mesma linha sem chaves.
when (foo) { true -> bar()
Ao usar uma longa lista de parâmetros, coloque a quebra de linha após o parêntese. Recue 4 espaços e agrupe os argumentos logicamente relacionados em uma linha.
drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true )
Use espaços ao redor do sinal de igual entre o nome do parâmetro e seu valor.
Ao usar chamadas em cadeia, coloque .
ou ?.
operadores em uma nova linha com um recuo em 4 espaços:
val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile { it is PsiComment || it is PsiWhiteSpace }
A primeira chamada na cadeia geralmente deve ter uma quebra de linha na frente, mas é normal não fazer isso se o código for melhor lido e fizer sentido.
Nas expressões lambda, os espaços devem ser usados ao redor de chaves e ao redor da seta que separa os parâmetros do corpo. Se uma chamada aceitar um único caractere lambda, ela deverá ser usada fora dos parênteses sempre que possível.
list.filter { it > 10 }
Ao atribuir um rótulo a uma expressão lambda, não coloque um espaço entre o rótulo e a chave de abertura:
fun foo() { ints.forEach lit@{
Ao declarar nomes de parâmetros em uma expressão lambda de várias linhas, coloque os nomes na primeira linha, depois na seta e na nova linha no início do corpo da função:
appendCommaSeparated(properties) { prop -> val propertyValue = prop.get(obj)
Se a lista de parâmetros não couber em uma linha, coloque a seta em uma linha separada:
foo { context: Context, environment: Env -> context.configureEnv(environment) }
Ao usar a documentação de várias linhas, coloque /**
em uma linha separada e inicie cada linha subseqüente com um asterisco:
Documentação curta pode ser colocada em uma linha:
Em geral, evite usar as tags param e return . Em vez disso, inclua uma descrição dos parâmetros e retorne valores diretamente no comentário da documentação e adicione referências aos parâmetros onde quer que sejam mencionados. Use param e retorne somente quando for necessária uma descrição longa que não se encaixe no significado do texto principal.
Evitando construções desnecessárias
Muitas construções sintáticas no Kotlin são opcionais e destacadas pelo ambiente de desenvolvimento como desnecessárias; você não deve usá-las no seu código apenas para tornar seu código "claro".
Use a palavra-chave Unidade
Nas funções, o uso da palavra-chave Unit não deve ser usado:
fun foo() {
Ponto e vírgula
Evite usar ponto e vírgula em todas as oportunidades.
Padrões de cadeia
Não use chaves ao inserir uma variável simples em uma sequência de modelos. Use chavetas apenas para expressões longas.
println("$name has ${children.size} children")
Uso linguístico de recursos do idioma
Imutabilidade
É preferível usar dados imutáveis antes de dados mutáveis. Sempre declare variáveis e propriedades locais como val
, não var
, a menos que elas realmente mudem.
Sempre use interfaces de coleção imutáveis ( Collection
, List
, Set
, Map
) para declarar coleções que não são alteradas. Em todas as oportunidades, ao usar métodos de fábrica para criar uma coleção, use uma implementação que retorne coleções imutáveis:
: .
.
[Type alias]
, , :
typealias MouseClickHandler = (Any, MouseEvent) -> Unit typealias PersonIndex = Map<String, Person>
-
-, , it
. - .
-
. - , . , - .
( @
) .
, , boolean
, .
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
try
, if
when
, return
:
return if (x) foo() else bar()
if
when
if
when
when (x) { null -> ... else -> ... } if (x == null) ... else ...
, when
.
Boolean?
Boolean?
, if (value == true)
if (value == false)
, if (value ?: false)
if (value != null && value)
.
filtet
, map
.. . : forEach
( for
null forEach
)
, , , .
until
( ):
for (i in 0..n - 1) { ... }
.
\n
escape- .
, trimIndent
, , trimMargin
, :
assertEquals( """ Foo Bar """.trimIndent(), value ) val a = """if(a > 1) { | return a |}""".trimMargin()
. , , .
:
. , , , , . API, , . , .
infix
, , . : and
, to
, zip
. : add
.
infix
, .
, , . , , . , , .
class Point(val x: Double, val y: Double) { companion object { fun fromPolar(angle: Double, radius: Double) = Point(...) } }
, , .
: , , Kotlin null
, null
public
/, , Kotlin:
fun apiCall(): String = MyJavaApi.getProperty("name")
(package-level class-level) Kotlin:
class Person { val name: String = MyJavaApi.getProperty("name") }
, , Kotlin :
fun main() { val name = MyJavaApi.getProperty("name") println(name) }
apply
/ with
/ run
/ also
/ let
Kotlin . , :
- ? , ,
it
, this
( also
let
). also
, .
- ? ,
apply
also
. , with
, let
run
.
- null ? ,
apply
, let
run
. , with
also
.
API:
- ( API)
- ( )
- KDoc public api, , /