Hola Habr! Les traigo a su atención la traducción del autor de la página de documentación de Convenciones de codificación de Kotlin de JetBrains.
→ Documentación original
Contenido:
Usando la guía de estilo en Intellij Idea
Para aplicar el formato en Intellij Idea de acuerdo con el manual actual, debe instalar el complemento Kotlin versión 1.2.20 o posterior, vaya a Configuración | Editor | Código de estilo | Kotlin, haga clic en el enlace "Establecer desde ..." en la esquina superior derecha y seleccione "Estilo predefinido" / Guía de estilo Kotlin "en el menú desplegable.
Para verificar que su código esté formateado de acuerdo con el estilo recomendado, vaya a la configuración de inspección y active la casilla "Kotlin | Problemas de estilo | El archivo no está formateado de acuerdo con la configuración del proyecto". Otras reglas de validación, como las convenciones de nomenclatura, están habilitadas de forma predeterminada.
Estructura del proyecto
Estructura de carpetas
En proyectos que utilizan diferentes idiomas, los archivos con código Kotlin deben estar en la misma carpeta que el código en otros idiomas y usar la misma estructura de archivos que se acepta para el idioma principal. Por ejemplo, para Java, los archivos deben estar en la estructura de carpetas de acuerdo con el nombre del paquete.
En proyectos que usan solo Kotlin, la estructura de carpetas recomendada es: usar carpetas para organizar paquetes con el directorio raíz omitido, es decir si todo el código del proyecto está en el paquete "org.example.kotlin" y sus paquetes, entonces los archivos fuente que pertenecen al paquete "org.example.kotlin" deben estar en el directorio raíz del proyecto, y los archivos con el código fuente del paquete "org. example.kotlin.foo.bar "debería estar en el subdirectorio" foo / bar "relativo a la raíz del proyecto.
Nombre de los archivos de origen.
Si el archivo Kotlin contiene solo una clase (posiblemente relacionada con la declaración de nivel superior), entonces debe nombrarse, así como una clase con la extensión .kt
. Si el archivo contiene varias clases o solo tiene declaraciones de nivel superior, elija un nombre que describa lo que contiene el archivo y asígnele el nombre correspondiente. Use la joroba de camello con una primera letra mayúscula para nombrar los archivos (por ejemplo, ProcessDeclarations.kt
).
El nombre del archivo debe describir lo que hace el código en el archivo. Es decir, debe evitar palabras sin sentido como "Util" para nombrar archivos.
Organizar archivos fuente
Colocar varias declaraciones (clases, funciones o propiedades de nivel superior) en el mismo archivo fuente de Kotlin es bienvenido si estas declaraciones están estrechamente relacionadas entre sí semánticamente y el tamaño del archivo sigue siendo razonable (no más de varios cientos de líneas).
En particular, al definir funciones de extensión para una clase que se aplican a todos los aspectos de la aplicación de esta clase, colóquelas en el mismo archivo donde se define la clase en sí. Al definir funciones de extensión que sean significativas solo para el contexto específico del uso de esta clase, colóquelas junto al código que usa la función de extensión. No cree archivos solo para almacenar "todas las extensiones de Foo".
Estructura de clase
Por lo general, el contenido de una clase se ordena en el siguiente orden:
- Declaraciones de propiedad y bloques de inicializador
- Constructores Secundarios
- Declaraciones de métodos
- Objetos acompañantes
No ordene las declaraciones de métodos alfabéticamente o visualmente, y no separe los métodos ordinarios de los métodos de extensión. En cambio, junte el código conectado lógicamente para que alguien que lea la clase de arriba a abajo pueda seguir la lógica de lo que sucede. Elija un orden de clasificación (código de nivel superior primero [cosas de nivel superior primero] y detalles más tarde o viceversa) y manténgalo.
Coloque las clases anidadas al lado del código que usa estas clases. Si las clases están destinadas para uso externo y no están referenciadas dentro de la clase, colóquelas al final después del objeto complementario.
Marco de implementación de interfaz
Al implementar una interfaz, mantenga la misma estructura que la interfaz que se está implementando (si es necesario, alternando con métodos privados adicionales utilizados para la implementación)
Anula estructura
Redefiniciones siempre juntas, una tras otra.
Reglas de nomenclatura
Kotlin sigue las mismas convenciones de nomenclatura que Java. En particular:
Nombres de paquete en minúsculas y no utilice guiones bajos (org.example.myproject). Por lo general, no se recomienda el uso de nombres de varias palabras, pero si necesita usar varias palabras, simplemente puede combinarlas o usar camello (org.examle.myProject).
Los nombres de mayúsculas y objetos comienzan con una letra mayúscula y usan joroba de camello:
open class DeclarationProcessor { ... } object EmptyDeclarationProcessor : DeclarationProcessor() { ... }
Nombre de la función
Los nombres de funciones, propiedades y variables locales comienzan con una letra minúscula y no contienen guiones bajos:
fun processDeclarations() { ... } var declarationCount = ...
Excepción: las funciones de fábrica utilizadas para crear instancias de clases pueden tener el mismo nombre que la clase que se está creando:
abstract class Foo { ... } class FooImpl : Foo { ... } fun Foo(): Foo { return FooImpl(...) }
Nombre de los métodos de prueba.
En las pruebas (y solo en las pruebas) está permitido usar nombres de métodos con espacios encerrados entre comillas. (Tenga en cuenta que dichos nombres de métodos no son compatibles actualmente con el tiempo de ejecución de Android). Los caracteres de subrayado en los nombres de métodos también están permitidos en el código de prueba.
class MyTestCase { @Test fun `ensure everything works`() { ... } @Test fun ensureEverythingWorks_onAndroid() { ... } }
Nombramiento de propiedad
Los nombres constantes (propiedades etiquetadas const
, o propiedades de nivel superior o un objeto val
sin una función de get
personalizada que contenga datos inmutables) deben escribirse en mayúsculas, separadas por guiones bajos:
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
Los nombres de nivel superior o las propiedades de objetos que contienen objetos con comportamiento o datos mutables deben usar nombres comunes en camello:
val mutableCollection: MutableSet<String> = HashSet()
Los nombres de propiedad que hacen referencia a objetos Singleton pueden usar el mismo estilo de denominación que las declaraciones de clase:
val PersonComparator: Comparator<Person> = ...
Para las enumeraciones, puede usar nombres escritos en mayúsculas separados por guiones bajos o en el estilo de joroba de camello, comenzando con una letra mayúscula, dependiendo del uso.
enum class Color { RED, GREEN }
enum class Color { RedColor, GreenColor }
Nota del traductor: simplemente no mezcle diferentes estilos. Elija un estilo y manténgalo en su diseño.
Nombramiento de propiedades ocultas
Si una clase tiene dos propiedades que son conceptualmente iguales, pero una es parte de la API pública y la otra es parte de la implementación, use el guión bajo como prefijo para el nombre de la propiedad oculta:
class C { private val _elementList = mutableListOf<Element>() val elementList: List<Element> get() = _elementList }
Elegir los nombres correctos
El nombre de la clase suele ser un sustantivo o una frase que explica qué es la clase:
List, PersonReader
El nombre del método suele ser una acción de verbo o frase que explica lo que hace el método:
close, readPersons
El nombre también debe indicar si el método cambia el objeto o devuelve uno nuevo. Por ejemplo, sort
es un tipo que cambia la colección, y sorted
es el retorno de una nueva copia ordenada de la colección.
Los nombres deben indicar claramente el propósito de la entidad, por lo que es mejor evitar el uso de palabras sin sentido ( Manager
, Wrapper
, etc.) en los nombres.
Cuando use el acrónimo como parte del nombre del anuncio, use letras mayúsculas si consta de dos letras ( IOStream
); o solo en mayúscula la primera letra, si es más larga ( XmlFormatter
, HttpInputStream
).
En la mayoría de los casos, Kotlin sigue las convenciones de formato de Java.
Use 4 espacios para sangrar. No use pestañas.
Para llaves, coloque la llave de apertura al final de la línea donde comienza la estructura y la llave de cierre en una línea separada, alineada horizontalmente con la estructura de apertura.
if (elements != null) { for (element in elements) {
(Nota: en Kotlin, un punto y coma es opcional, por lo que el ajuste de línea es importante. El diseño del lenguaje asume llaves rizadas estilo Java, y puede encontrar un comportamiento inesperado de ejecución de código si intenta usar un estilo de formato diferente).
Espacios horizontales
Coloque espacios alrededor de operadores binarios (a + b)
. Excepción: no coloque espacios alrededor del operador "rango a" (0..i)
No coloque espacios alrededor de operadores unarios (a++)
Coloque espacios entre las palabras clave de control ( if
, when
, for
y while
) y los corchetes de apertura correspondientes.
No ponga un espacio antes del paréntesis de apertura en la declaración primaria de un constructor, método o método.
class A(val x: Int) fun foo(x: Int) { ... } fun bar() { foo(1) }
Nunca ponga un espacio después de (
, [
o antes ]
, )
.
Nunca coloque espacio alrededor de un punto .
u operador ?.
:
foo.bar().filter { it > 2 }.joinToString() foo?.()
Pon un espacio después de la doble barra para el comentario //
:
No ponga espacios alrededor de los corchetes angulares utilizados para indicar los parámetros de tipo:
Class Map<K, V> { ... }
No ponga espacios alrededor de los dos puntos dobles para indicar una referencia al método ::
class:
Foo::class String::length
No poner un espacio antes ?
usado para marcar un null
:
String?
En general, evite cualquier tipo de alineación horizontal. Cambiar el nombre de un identificador a un nombre de una longitud diferente no debería afectar el formato del código.
Colon
Pon un espacio antes del colon :
en los siguientes casos:
- cuando se usa para separar el tipo del supertipo;
abstract class Foo<out T : Any>
- al delegar a un constructor de superclase u otro constructor de la misma clase;
constructor(x: String) : super(x) { ... } constructor(x: String) : this(x) { ... }
- después del objeto de palabra clave.
val x = object : IFoo { ... }
No ponga un espacio antes :
cuando separa un anuncio y su tipo.
abstract fun foo(a: Int): T
Siempre ponga un espacio después de :
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 { ... } }
Las clases con varios parámetros básicos de constructor y nombres cortos se pueden escribir en una línea:
class Person(id: Int, name: String)
Las clases con nombres más largos o el número de parámetros deben formatearse de modo que cada parámetro principal del constructor esté en una línea separada con sangría. Además, el soporte de cierre debe estar en una nueva línea. Si usamos la herencia, la llamada al constructor de la superclase o la lista de interfaces implementadas debe ubicarse en la misma línea que el paréntesis:
class Person( id: Int, name: String, surname: String ) : Human(id, name) { ... }
Al especificar la interfaz y llamar al constructor de la superclase, primero debe ubicarse el constructor de la superclase, luego el nombre de la interfaz en una nueva línea se justifica a la izquierda:
class Person( id: Int, name: String, surname: String ) : Human(id, name), KotlinMaker { ... }
Para las clases con una larga lista de supertipos, debe poner un salto de línea después de los dos puntos y alinear todos los nombres de supertipos horizontalmente a la izquierda:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
Para separar claramente el encabezado de clase y su cuerpo cuando el encabezado de clase es largo, coloque una línea vacía después del encabezado de clase (como en el ejemplo anterior) o coloque la llave de apertura en una línea separada:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
Use sangría regular (4 espacios) para los parámetros del constructor.
Justificación: esto asegura que las propiedades declaradas en el constructor principal tengan la misma sangría que las propiedades declaradas en el cuerpo de la clase.
Modificadores
Si un anuncio contiene múltiples modificadores, organícelos siempre en el siguiente orden:
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
Ponga todas las anotaciones antes de los modificadores:
@Named("Foo") private val foo: Foo
Si no está trabajando en una biblioteca, omita los modificadores redundantes (por ejemplo, público).
Las anotaciones generalmente se colocan en líneas separadas antes de la declaración a la que están adjuntas, y con el mismo guión:
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
Las anotaciones sin argumentos se pueden ubicar en una línea:
@JsonExclude @JvmField var x: String
Se puede colocar una anotación sin argumentos en la misma línea que la declaración correspondiente:
@Test fun foo() { ... }
Anotaciones de archivo
Las anotaciones a los archivos se colocan después del comentario en el archivo (si corresponde), antes de la declaración del paquete y están separadas del paquete por una línea vacía (para enfatizar el hecho de que están dirigidas al archivo, no al paquete).
@file:JvmName("FooBar") package foo.bar
Si la firma del método no cabe en una línea, use la siguiente sintaxis:
fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType ): ReturnType {
Use sangría regular (4 espacios) para los parámetros de la función.
Justificación: coherencia con los parámetros del constructor.
Es preferible usar una expresión sin llaves para funciones que consisten en una línea.
fun foo(): Int {
Si el cuerpo de una función de una sola línea no cabe en la misma línea que la declaración, coloque el signo = en la primera línea. Sangra el cuerpo de la expresión por 4 espacios.
fun f(x: String) = x.length
Para propiedades simples de solo lectura, es preferible utilizar el formato de una sola línea:
val isEmpty: Boolean get() = size == 0
Para propiedades más complejas, use siempre get
y set
en líneas separadas:
val foo: String get() { ... }
Para las propiedades con inicialización, si el inicializador es demasiado largo, agregue un salto de línea después del signo igual y una sangría de cuatro espacios para la cadena de inicialización:
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
Si la condición en la instrucción de control if
o when
es de varias líneas, use siempre llaves alrededor del cuerpo de la instrucción. Sangra cada línea subsiguiente de la condición por 4 espacios con relación al inicio de la declaración. Coloque los corchetes de cierre de la condición junto con la llave de apertura en una línea separada:
if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) ) { return createKotlinNotConfiguredPanel(module) }
Justificación: alineación ordenada y separación clara de la condición corporal y la condición corporal
Coloque las palabras clave else
, catch
, finally
, así como la palabra clave while del bucle do / while en la misma línea que el corchete de cierre anterior:
if (condition) {
Si las condiciones de las instrucciones consisten en varios bloques, se recomienda separarlas entre sí con una línea vacía:
private fun parsePropertyValue(propName: String, token: Token) { when (token) { is Token.ValueToken -> callback.visitValue(propName, token.value) Token.LBRACE -> {
Coloque los bloques cortos de las declaraciones when
en la misma línea sin llaves.
when (foo) { true -> bar()
Cuando use una larga lista de parámetros, coloque el salto de línea después del paréntesis. Sangra 4 espacios y agrupa los argumentos relacionados lógicamente en una línea.
drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true )
Use espacios alrededor del signo igual entre el nombre del parámetro y su valor.
Cuando use llamadas encadenadas, ponga .
o operadores en una nueva línea con una sangría en 4 espacios:
val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile { it is PsiComment || it is PsiWhiteSpace }
La primera llamada en la cadena generalmente debería tener un salto de línea en frente, pero es normal no hacerlo si el código se lee mejor y tiene sentido.
En las expresiones lambda, se deben usar espacios alrededor de llaves y alrededor de la flecha que separa los parámetros del cuerpo. Si una llamada acepta un solo carácter lambda, debe usarse fuera del paréntesis siempre que sea posible.
list.filter { it > 10 }
Al asignar una etiqueta a una expresión lambda, no coloque un espacio entre la etiqueta y la llave de apertura:
fun foo() { ints.forEach lit@{
Al declarar nombres de parámetros en una expresión lambda de varias líneas, coloque los nombres en la primera línea, luego en la flecha y en la nueva línea al comienzo del cuerpo de la función:
appendCommaSeparated(properties) { prop -> val propertyValue = prop.get(obj)
Si la lista de parámetros no cabe en una línea, coloque la flecha en una línea separada:
foo { context: Context, environment: Env -> context.configureEnv(environment) }
Cuando use documentación de varias líneas, coloque /**
en una línea separada y comience cada línea subsiguiente con un asterisco:
Se puede colocar una breve documentación en una línea:
En general, evite usar las etiquetas param y return . En su lugar, incluya una descripción de los parámetros y valores de retorno directamente en el comentario de la documentación y agregue referencias de parámetros donde sea que se mencionen. Use param y return solo cuando se requiera una descripción larga que no se ajuste al significado del texto principal.
Evitar construcciones innecesarias
Muchas construcciones sintácticas en Kotlin son opcionales y el entorno de desarrollo las resalta como innecesarias; no debe usarlas en su código solo para que su código sea "claro".
Use la palabra clave Unidad
En funciones, el uso de la palabra clave Unit no debe usarse:
fun foo() {
Punto y coma
Evite usar un punto y coma en cada oportunidad.
Patrones de cuerda
No use llaves para insertar una variable simple en una cadena de plantilla. Use llaves entre comillas solo para expresiones largas.
println("$name has ${children.size} children")
Uso idiomático de las características del lenguaje.
Inmutabilidad
Es preferible utilizar datos inmutables antes que datos mutables. Siempre declare las variables y propiedades locales como val
, no var
, a menos que realmente cambien.
Utilice siempre interfaces de colección inmutables ( Collection
, List
, Set
, Map
) para declarar colecciones que no cambian. En cada oportunidad, cuando use métodos de fábrica para crear una colección, use una implementación que devuelva colecciones inmutables:
: .
.
[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, , /