This document serves as the complete definition of Google's Android coding standards for source code in the Kotlin Programming Language. A Kotlin source file is described as being in Google Android Style if and only if it adheres to the rules herein.
Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this document focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn't clearly enforceable (whether by human or tool).
Последнее обновление: 06.09.2023
Исходные файлы
Все исходные файлы должны быть закодированы в UTF-8.
Название
If a source file contains only a single top-level class, the file name should reflect the case-sensitive name plus the .kt extension. Otherwise, if a source file contains multiple top-level declarations, choose a name that describes the contents of the file, apply PascalCase (camelCase is acceptable if the filename is plural), and append the .kt extension.
// MyClass.kt class MyClass { }
// Bar.kt class Bar { } fun Runnable.toBar(): Bar = Bar()
// Map.kt fun <T, O> Set<T>.map(func: (T) -> O): List<O> = emptyList() fun <T, O> List<T>.map(func: (T) -> O): List<O> = emptyList()
// extensions.kt fun MyClass.process() = { /* ... */ } fun MyResult.print() = { /* ... */ }
Особые персонажи
Символы пустого пространства
Aside from the line terminator sequence, the ASCII horizontal space character (0x20) is the only whitespace character that appears anywhere in a source file. This implies that:
- All other whitespace characters in string and character literals are escaped.
- Символы табуляции не используются для отступов.
Специальные последовательности побега
For any character that has a special escape sequence ( \b , \n , \r , \t , \' , \" , \\ , and \$ ), that sequence is used rather than the corresponding Unicode (eg, \u000a ) escape.
Несимволы ASCII
For the remaining non-ASCII characters, either the actual Unicode character (eg, ∞ ) or the equivalent Unicode escape (eg, \u221e ) is used. The choice depends only on which makes the code easier to read and understand. Unicode escapes are discouraged for printable characters at any location and are strongly discouraged outside of string literals and comments.
| Пример | Обсуждение |
|---|---|
val unitAbbrev = "μs" | Лучше всего: совершенно понятно даже без комментариев. |
val unitAbbrev = "\u03bcs" // μs | Poor: there's no reason to use an escape with a printable character. |
val unitAbbrev = "\u03bcs" | Плохо: читатель понятия не имеет, что это такое. |
return "\ufeff" + content | Good: use escapes for non-printable characters, and comment if necessary. |
Структура
Файл .kt содержит следующие элементы в указанном порядке:
- Заголовок, содержащий информацию об авторских правах и/или лицензии (необязательно)
- Аннотации на уровне файлов
- Заявление о упаковке
- Импорт операторов
- Декларации верхнего уровня
Exactly one blank line separates each of these sections.
Авторские права / Лицензия
If a copyright or license header belongs in the file it should be placed at the immediate top in a multi-line comment.
/* * Copyright 2017 Google, Inc. * * ... */
Do not use a KDoc-style or single-line-style comment.
/** * Copyright 2017 Google, Inc. * * ... */
// Copyright 2017 Google, Inc. // // ...
Аннотации на уровне файлов
Annotations with the "file" use-site target are placed between any header comment and the package declaration.
Заявление о упаковке
The package statement is not subject to any column limit and is never line-wrapped.
Импорт операторов
Import statements for classes, functions, and properties are grouped together in a single list and ASCII sorted.
Импорт подстановочных символов (любого типа) не допускается.
Similar to the package statement, import statements are not subject to a column limit and they are never line-wrapped.
Декларации верхнего уровня
A .kt file can declare one or more types, functions, properties, or type aliases at the top-level.
The contents of a file should be focused on a single theme. Examples of this would be a single public type or a set of extension functions performing the same operation on multiple receiver types. Unrelated declarations should be separated into their own files and public declarations within a single file should be minimized.
No explicit restriction is placed on the number nor order of the contents of a file.
Source files are usually read from top-to-bottom meaning that the order, in general, should reflect that the declarations higher up will inform understanding of those farther down. Different files may choose to order their contents differently. Similarly, one file may contain 100 properties, another 10 functions, and yet another a single class.
What is important is that each file uses some logical order, which its maintainer could explain if asked. For example, new functions are not just habitually added to the end of the file, as that would yield “chronological by date added” ordering, which is not a logical ordering.
Заказ для членов класса
The order of members within a class follow the same rules as the top-level declarations.
Форматирование
Брекеты
Braces are not required for when branches and if expressions which have no more than one else branch and which fit on a single line.
if (string.isEmpty()) return val result = if (string.isEmpty()) DEFAULT_VALUE else string when (value) { 0 -> return // … }
Braces are otherwise required for any if , for , when branch, do , and while statements and expressions, even when the body is empty or contains only a single statement.
if (string.isEmpty()) return // WRONG! if (string.isEmpty()) { return // Okay } if (string.isEmpty()) return // WRONG else doLotsOfProcessingOn(string, otherParametersHere) if (string.isEmpty()) { return // Okay } else { doLotsOfProcessingOn(string, otherParametersHere) }
Непустые блоки
Braces follow the Kernighan and Ritchie style ("Egyptian brackets") for nonempty blocks and block-like constructs:
- Перед открывающей скобкой перенос строки не требуется.
- Разрыв строки после начальной скобки.
- Разрыв строки перед закрывающей скобкой.
- Line break after the closing brace, only if that brace terminates a statement or terminates the body of a function, constructor, or named class. For example, there is no line break after the brace if it is followed by
elseor a comma.
return Runnable { while (condition()) { foo() } }
return object : MyClass() { override fun foo() { if (condition()) { try { something() } catch (e: ProblemException) { recover() } } else if (otherCondition()) { somethingElse() } else { lastThing() } } }
A few exceptions for enum classes are given below.
Пустые блоки
An empty block or block-like construct must be in K&R style.
try { doSomething() } catch (e: Exception) {} // WRONG!
try { doSomething() } catch (e: Exception) { } // Okay
Выражения
An if/else conditional that is used as an expression may omit braces only if the entire expression fits on one line.
val value = if (string.isEmpty()) 0 else 1 // Okay
val value = if (string.isEmpty()) // WRONG! 0 else 1
val value = if (string.isEmpty()) { // Okay 0 } else { 1 }
Отступ
Each time a new block or block-like construct is opened, the indent increases by four spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block.
Одно утверждение на строку
Each statement is followed by a line break. Semicolons are not used.
Перенос строки
Code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained below.
Исключения:
- Lines where obeying the column limit is not possible (for example, a long URL in KDoc)
- операторы
packageиimport - Командные строки в комментарии, которые можно скопировать и вставить в командную оболочку.
Где сделать перерыв
The prime directive of line-wrapping is: prefer to break at a higher syntactic level. Also:
- When a line is broken at an operator or infix function name, the break comes after the operator or infix function name.
- When a line is broken at the following “operator-like” symbols, the break comes before the symbol:
- Разделитель точек (
.,?.). - Два двоеточия в ссылке на элемент (
::).
- Разделитель точек (
- Имя метода или конструктора остается прикрепленным к открывающей скобке
(), следующей за ним. - A comma (
,) stays attached to the token that precedes it. - Стрелка лямбда (
->) остается прикрепленной к списку аргументов, предшествующему ей.
Функции
When a function signature does not fit on a single line, break each parameter declaration onto its own line. Parameters defined in this format should use a single indent (+4). The closing parenthesis ( ) ) and return type are placed on their own line with no additional indent.
fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "" ): String { // ... }
Функции выражений
When a function contains only a single expression it can be represented as an expression function .
override fun toString(): String { return "Hey" }
override fun toString(): String = "Hey"
Характеристики
Если инициализатор свойства не помещается на одной строке, сделайте перенос строки после знака равенства ( = ) и используйте отступ.
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
Свойства, объявляющие функции get и/или set должны располагаться на отдельной строке с обычным отступом (+4). Форматируйте их, используя те же правила, что и функции.
var directory: File? = null set(value) { // … }
val defaultExtension: String get() = "kt"
Пространство
Вертикальный
Появляется одна пустая строка:
- Между последовательными членами класса: свойства, конструкторы, функции, вложенные классы и т. д.
- Exception: A blank line between two consecutive properties (having no other code between them) is optional. Such blank lines are used as needed to create logical groupings of properties and associate properties with their backing property, if present.
- Exception: Blank lines between enum constants are covered below.
- Между операторами, по мере необходимости , для организации кода в логические подразделы.
- При желании его можно разместить перед первым оператором в функции, перед первым членом класса или после последнего члена класса (это не рекомендуется и не запрещается).
- В соответствии с требованиями других разделов данного документа (например, раздела «Структура »).
Допускается наличие нескольких пустых строк подряд, но это не рекомендуется и никогда не является обязательным.
Горизонтальный
Помимо случаев, когда это требуется правилами языка или другими стилистическими нормами, и за исключением литералов, комментариев и KDoc, один пробел в кодировке ASCII также встречается только в следующих местах:
- Разделение любого зарезервированного слова, такого как
if,forилиcatchот открывающей скобки(), следующей за ним в этой строке.// WRONG! for(i in 0..1) { }
// Okay for (i in 0..1) { }
- Разделение любого зарезервированного слова, такого как
elseилиcatch, закрывающей фигурной скобкой (}), которая предшествует ему в этой строке.// WRONG! }else { }
// Okay } else { }
- Перед любой открывающей фигурной скобкой (
{).// 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() }
- два двоеточия (
::) в ссылке на элемент.// WRONG! val toString = Any :: toString
// Okay val toString = Any::toString
- точечный разделитель (
.).// WRONG it . toString()
// Okay it.toString()
- оператор диапазона (
..).// WRONG for (i in 1 .. 4) { print(i) }
// Okay for (i in 1..4) { print(i) }
- стрелка в лямбда-выражении (
- Перед двоеточием (
:) только в том случае, если оно используется в объявлении класса для указания базового класса или интерфейсов, или в предложенииwhereдля общих ограничений .// WRONG! class Foo: Runnable
// Okay class Foo : Runnable
// WRONG fun <T: Comparable> max(a: T, b: T)
// Okay fun <T : Comparable<T>> 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
- По обе стороны от двойной косой черты (
//) начинается комментарий в конце строки. Здесь допускается несколько пробелов, но это не обязательно.// WRONG! var debugging = false//disabled by default
// Okay var debugging = false // disabled by default
Это правило ни в коем случае не следует толковать как требование или запрет дополнительного пространства в начале или конце строки; оно касается только внутреннего пространства.
Конкретные конструкции
Классы перечисления
Перечисление, не содержащее функций и документации по своим константам, может быть опционально отформатировано в одну строку.
enum class Answer { YES, NO, MAYBE }
Когда константы в перечислении размещаются на отдельных строках, пустая строка между ними не требуется, за исключением случаев, когда они определяют тело перечисления.
enum class Answer { YES, NO, MAYBE { override fun toString() = """¯\_(ツ)_/¯""" } }
Поскольку перечисления являются классами, все остальные правила форматирования классов остаются в силе.
Аннотации
Аннотации элементов или типов размещаются на отдельных строках непосредственно перед аннотируемой конструкцией.
@Retention(SOURCE) @Target(FUNCTION, PROPERTY_SETTER, FIELD) annotation class Global
Аннотации без аргументов можно размещать на одной строке.
@JvmField @Volatile var disposable: Disposable? = null
Если присутствует только одна аннотация без аргументов, её можно разместить на той же строке, что и объявление.
@Volatile var disposable: Disposable? = null @Test fun selectAll() { // … }
Синтаксис @[...] может использоваться только с явно указанной целью использования и только для объединения двух или более аннотаций без аргументов в одной строке.
@field:[JvmStatic Volatile] var disposable: Disposable? = null
Типы неявной доходности/свойств
Если тело функции-выражения или инициализатор свойства представляют собой скалярное значение, или тип возвращаемого значения можно однозначно определить из тела функции, то их можно опустить.
override fun toString(): String = "Hey" // becomes override fun toString() = "Hey"
private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png") // becomes private val ICON = IconLoader.getIcon("/icons/kotlin.png")
При разработке библиотеки сохраняйте явное объявление типа, если оно является частью публичного API.
Название
Идентификаторы используют только буквы и цифры ASCII, а в небольшом числе случаев, отмеченных ниже, — символы подчеркивания. Таким образом, каждое допустимое имя идентификатора сопоставляется с помощью регулярного выражения \w+ .
Специальные префиксы или суффиксы, подобные тем, что встречаются в примерах name_ , mName , s_name и kName , не используются, за исключением случаев, когда речь идёт о свойствах-заглушках (см. Свойства-заглушки ).
Названия пакетов
Названия пакетов пишутся строчными буквами, а последовательные слова просто соединяются вместе (без подчеркиваний).
// Okay package com.example.deepspace // WRONG! package com.example.deepSpace // WRONG! package com.example.deep_space
Названия типов
Class names are written in PascalCase and are typically nouns or noun phrases. For example, Character or ImmutableList . Interface names may also be nouns or noun phrases (for example, List ), but may sometimes be adjectives or adjective phrases instead (for example Readable ).
Классы тестов именуются, начиная с имени тестируемого класса и заканчивая на Test . Например, HashTest или HashIntegrationTest .
Названия функций
Названия функций записываются в стиле camelCase и обычно представляют собой глаголы или глагольные фразы. Например, sendMessage или stop .
Underscores are permitted to appear in test function names to separate logical components of the name.
@Test fun pop_emptyStack() { // … }
Functions annotated with @Composable that return Unit are PascalCased and named as nouns, as if they were types.
@Composable fun NameTag(name: String) { // … }
Function names should not contain spaces because this is not supported on every platform (notably, this is not fully supported in Android).
// WRONG! fun `test every possible case`() {} // OK fun testEveryPossibleCase() {}
Постоянные имена
Constant names use UPPER_SNAKE_CASE: all uppercase letters, with words separated by underscores. But what is a constant, exactly?
Constants are val properties with no custom get function, whose contents are deeply immutable, and whose functions have no detectable side-effects. This includes immutable types and immutable collections of immutable types as well as scalars and string if marked as const . If any of an instance's observable state can change, it is not a constant. Merely intending to never mutate the object is not enough.
const val NUMBER = 5 val NAMES = listOf("Alice", "Bob") val AGES = mapOf("Alice" to 35, "Bob" to 32) val COMMA_JOINED = NAMES.joinToString(", ") val EMPTY_ARRAY = arrayOf<SomeMutableType>()
Эти имена, как правило, представляют собой существительные или именные группы.
Constant values can only be defined inside of an object or as a top-level declaration. Values otherwise meeting the requirement of a constant but defined inside of a class must use a non-constant name.
Constants which are scalar values must use the const modifier .
Непостоянные имена
Non-constant names are written in camelCase. These apply to instance properties, local properties, and parameter names.
val variable = "var" val nonConstScalar = "non-const" val mutableCollection: MutableSet<String> = HashSet() val mutableElements = listOf(mutableInstance) val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2) val logger = Logger.getLogger(MyClass::class.java.name) val nonEmptyArray = arrayOf("these", "can", "change")
Эти имена, как правило, представляют собой существительные или именные группы.
Свойства резервной копии
When a backing property is needed, its name should exactly match that of the real property except prefixed with an underscore.
private var _table: Map<String, Int>? = null val table: Map<String, Int> get() { if (_table == null) { _table = HashMap() } return _table ?: throw AssertionError() }
Типы имен переменных
Each type variable is named in one of two styles:
- A single capital letter, optionally followed by a single numeral (such as
E,T,X,T2) - A name in the form used for classes, followed by the capital letter
T(such asRequestT,FooBarT)
Чехол для верблюда
Sometimes there is more than one reasonable way to convert an English phrase into camel case, such as when acronyms or unusual constructs like “IPv6” or “iOS” are present. To improve predictability, use the following scheme.
Начнём с прозаической формы имени:
- Convert the phrase to plain ASCII and remove any apostrophes. For example, “Müller's algorithm” might become “Muellers algorithm”.
- Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens). Recommended: if any word already has a conventional camel-case appearance in common usage, split this into its constituent parts (eg, “AdWords” becomes “ad words”). Note that a word such as “iOS” is not really in camel case per se; it defies any convention, so this recommendation does not apply.
- Now lowercase everything (including acronyms), then do one of the following:
- Uppercase the first character of each word to yield pascal case.
- Uppercase the first character of each word except the first to yield camel case.
- Finally, join all the words into a single identifier.
Note that the casing of the original words is almost entirely disregarded.
| Прозаическая форма | Правильный | Неверно |
|---|---|---|
| "XML HTTP-запрос" | XmlHttpRequest | XMLHTTPRequest |
| «новый идентификатор клиента» | newCustomerId | newCustomerID |
| «внутренний секундомер» | innerStopwatch | innerStopWatch |
| "Поддерживает IPv6 на iOS" | supportsIpv6OnIos | supportsIPv6OnIOS |
| "Импортер YouTube" | YouTubeImporter | YoutubeImporter * |
(* Допустимо, но не рекомендуется.)
Документация
Форматирование
The basic formatting of KDoc blocks is seen in this example:
/** * Multiple lines of KDoc text are written here, * wrapped normally… */ fun method(arg: String) { // … }
...или в этом примере в одну строку:
/** An especially short bit of KDoc. */
The basic form is always acceptable. The single-line form may be substituted when the entirety of the KDoc block (including comment markers) can fit on a single line. Note that this only applies when there are no block tags such as @return .
Абзацы
One blank line—that is, a line containing only the aligned leading asterisk ( * )—appears between paragraphs, and before the group of block tags if present.
Теги блоков
Any of the standard “block tags” that are used appear in the order @constructor , @receiver , @param , @property , @return , @throws , @see , and these never appear with an empty description. When a block tag doesn't fit on a single line, continuation lines are indented 4 spaces from the position of the @ .
Краткий обзор
Each KDoc block begins with a brief summary fragment. This fragment is very important: it is the only part of the text that appears in certain contexts such as class and method indexes.
This is a fragment–a noun phrase or verb phrase, not a complete sentence. It does not begin with " A `Foo` is a... ", or " This method returns... ", nor does it have to form a complete imperative sentence like " Save the record. ". However, the fragment is capitalized and punctuated as if it were a complete sentence.
Использование
At the minimum, KDoc is present for every public type, and every public or protected member of such a type, with a few exceptions noted below.
Исключение: Функции, не требующие пояснений.
KDoc is optional for “simple, obvious” functions like getFoo and properties like foo , in cases where there really and truly is nothing else worthwhile to say but “Returns the foo”.
It is not appropriate to cite this exception to justify omitting relevant information that a typical reader might need to know. For example, for a function named getCanonicalName or property named canonicalName , don't omit its documentation (with the rationale that it would say only /** Returns the canonical name. */ ) if a typical reader may have no idea what the term "canonical name" means!
Исключение: переопределения
KDoc is not always present on a method that overrides a supertype method.