Рекомендации по стилю кодирования
Общие и легко соблюдаемые рекомендации по стилю кодирования имеют важное значение для любого языка программирования. Здесь мы предоставляем рекомендации по стилю кода и организации кода для проектов, использующих Kotlin.
Настройка стиля в IDE
Две наиболее популярные IDE для Kotlin — IntelliJ IDEA и Android Studio — предоставляют мощную поддержку форматирования кода. Вы можете настроить их на автоматическое форматирование кода в соответствии с заданным стилем.
Применение руководства по стилю
Перейдите в Настройки/Предпочтения | Редактор | Стиль кода | Kotlin.
Нажмите Установить из....
Выберите Руководство по стилю Kotlin.
Проверка соответствия кода руководству по стилю
Перейдите в Настройки/Предпочтения | Редактор | Проверки | Kotlin.
Откройте Kotlin | Вопросы стиля.
Включите проверку Файл не отформатирован в соответствии с настройками проекта. Дополнительные проверки, которые проверяют другие вопросы, описанные в руководстве по стилю (например, соглашения об именовании), включены по умолчанию.
Организация исходного кода
Структура каталогов
В чистых проектах Kotlin рекомендуемая структура каталогов следует структуре пакетов с исключением общего корневого пакета. Например, если весь код проекта находится в org.example.kotlin пакете и его подпакетах, файлы с org.example.kotlin пакетом должны располагаться непосредственно под корнем источника, а файлы в org.example.kotlin.network.socket должны находиться в подкаталоге network/socket корня источника.
Имена файлов исходного кода
Если файл Kotlin содержит один класс или интерфейс (возможно, с соответствующими объявлениями на верхнем уровне), его имя должно совпадать с именем класса с добавлением расширения .kt. Это относится ко всем типам классов и интерфейсов. Если файл содержит несколько классов или только объявления на верхнем уровне, выберите имя, описывающее содержимое файла, и назовите файл соответственно. Используйте верхний регистр с заглавной буквы (также известный как PascalCase), например, ProcessDeclarations.kt.
Имя файла должно описывать, что делает код в файле. Поэтому следует избегать использования бессмысленных слов, таких как Util в именах файлов.
Организация файлов исходного кода
Размещение нескольких объявлений (классов, функций или свойств верхнего уровня) в одном файле исходного кода Kotlin приветствуется, если эти объявления тесно связаны между собой семантически, и размер файла остается разумным (не превышает нескольких сотен строк).
В частности, при определении расширяющих функций для класса, которые актуальны для всех клиентов этого класса, помещайте их в тот же файл, что и сам класс. При определении расширяющих функций, которые имеют смысл только для конкретного клиента, помещайте их рядом с кодом этого клиента. Избегайте создания файлов только для хранения всех расширений какого-либо класса.
Макет класса
Содержимое класса должно располагаться в следующем порядке:
Объявления свойств и инициализационные блоки
Вторичные конструкторы
Объявления методов
Компаньон-объект
Не сортируйте объявления методов по алфавиту или видимостью и не отделяйте обычные методы от расширяющих методов. Вместо этого, группируйте взаимосвязанные элементы, чтобы при чтении класса сверху вниз можно было проследить логику происходящего. Выберите порядок (либо сначала элементы высшего уровня, либо наоборот) и придерживайтесь его.
Поместите вложенные классы рядом с кодом, использующим эти классы. Если классы предназначены для внешнего использования и не ссылаются внутри класса, поместите их в конце, после компаньон-объекта.
Макет реализации интерфейса
При реализации интерфейса сохраняйте реализуемые члены в том же порядке, что и члены интерфейса (при необходимости, перемежая дополнительные частные методы, используемые для реализации).
Макет перегрузки
Всегда располагайте перегрузки рядом друг с другом в классе.
Правила именования
Правила именования пакетов и классов в Kotlin довольно просты:
Имена пакетов всегда пишутся строчными буквами и не содержат подчеркиваний (
org.example.project). Использование многословных имён в целом не рекомендуется, но если вам нужно использовать несколько слов, вы можете либо просто объединить их вместе, либо использовать нижний регистр (org.example.myProject).Имена классов и объектов начинаются с заглавной буквы и используют нижний регистр с заглавной буквы:
open class DeclarationProcessor { /*...*/ }
object EmptyDeclarationProcessor : DeclarationProcessor() { /*...*/ }
Имена функций
Имена функций, свойств и локальных переменных начинаются со строчной буквы, используют нижний регистр с заглавной буквы и не содержат подчеркиваний:
fun processDeclarations() { /*...*/ }
var declarationCount = 1
Исключение: функции-фабрики, используемые для создания экземпляров классов, могут иметь то же имя, что и абстрактный возвращаемый тип:
interface Foo { /*...*/ }
class FooImpl : Foo { /*...*/ }
fun Foo(): Foo { return FooImpl() }
Имена методов для тестов
В тестах (и только в тестах) вы можете использовать имена методов со пробелами, заключёнными в обратные кавычки. Обратите внимание, что такие имена методов в настоящее время не поддерживаются Android-средой выполнения. Подчеркивания в именах методов также разрешены в тестовом коде.
class MyTestCase {
@Test fun `ensure everything works`() { /*...*/ }
@Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}
Имена свойств
Имена констант (свойства, помеченные const, или свойства верхнего уровня или объекта val без пользовательской функции get, которые хранят неизменяемые данные), должны использовать имена с прописными буквами, разделенными подчеркиваниями (screaming snake case):
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
Имена свойств верхнего уровня или объекта, которые хранят объекты с поведением или изменяемыми данными, должны использовать имена с нижним регистром с заглавной буквы:
val mutableCollection: MutableSet<String> = HashSet()
Имена свойств, хранящих ссылки на объекты-синглтоны, могут использовать тот же стиль именования, что и объявления object:
val PersonComparator: Comparator<Person> = /*...*/
Для констант перечислений допустимо использование либо имён с прописными буквами, разделённых символом нижнего подчеркивания (screaming snake case) (enum class Color { RED, GREEN }) или имён с верхним регистром с заглавной буквы, в зависимости от использования.
Имена для свойств-обработчиков
Если у класса есть два свойства, которые концептуально одинаковы, но одно является частью публичного API, а другое — деталью реализации, используйте символ подчеркивания в качестве префикса для имени приватного свойства:
class C {
private val _elementList = mutableListOf<Element>()
val elementList: List<Element>
get() = _elementList
}
Выбор хороших имён
Имя класса обычно является существительным или словосочетанием, объясняющим, что представляет собой класс: List, PersonReader.
Имя метода обычно является глаголом или глагольным словосочетанием, описывающим, что делает метод: close, readPersons. Имя также должно указывать, изменяет ли метод объект или возвращает новый. Например, sort сортирует коллекцию на месте, а sorted возвращает отсортированную копию коллекции.
Имена должны ясно указывать назначение сущности, поэтому лучше избегать использования бессмысленных слов (Manager, Wrapper ) в именах.
При использовании аббревиатуры в качестве части имени объявления, записывайте её заглавными буквами, если она состоит из двух букв (IOStream); записывайте её с заглавной буквы только первой, если она длиннее (XmlFormatter, HttpInputStream).
Форматирование
Отступы
Используйте четыре пробела для отступов. Не используйте табуляцию.
Для фигурных скобок поместите открывающую фигурную скобку в конце строки, где начинается конструкция, а закрывающую фигурную скобку в отдельной строке, выровненной по горизонтали с открывающей.
if (elements != null) {
for (element in elements) {
// ...
}
}
Горизонтальные пробелы
Размещайте пробелы вокруг бинарных операторов (
a + b). Исключение: не ставьте пробелы вокруг оператора «диапазон до» (0..i).Не ставьте пробелы вокруг унарных операторов (
a++).Размещайте пробелы между ключевыми словами управления потоком (
if,when,for, иwhile) и соответствующей открывающей скобкой.Не ставьте пробел перед открывающей скобкой в объявлении основного конструктора, объявлении метода или вызове метода.
class A(val x: Int)
fun foo(x: Int) { ... }
fun bar() {
foo(1)
}
Никогда не ставьте пробел после
(,[, или перед],)Никогда не ставьте пробелы вокруг
.или?.:foo.bar().filter { it > 2 }.joinToString(),foo?.bar()Поставьте пробел после
//:// This is a commentНе ставьте пробелы вокруг угловых скобок, используемых для указания параметров типа:
class Map<K, V> { ... }Не ставьте пробелы вокруг
:::Foo::class,String::lengthНе ставьте пробел перед
?используемым для маркировки необязательного типа:String?
В качестве общего правила, избегайте горизонтального выравнивания любого рода. Переименование идентификатора на имя с другой длиной не должно влиять на форматирование ни объявления, ни его использования.
Двоеточие
Поставьте пробел перед : в следующих случаях:
когда он используется для разделения типа и супертипа
при делегировании в конструктор суперкласса или другой конструктор того же класса
после ключевого слова
object
Не ставьте пробел перед : при разделении объявления и его типа.
Всегда ставьте пробел после :.
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 { /*...*/ }
}
Заголовки классов
Классы с несколькими параметрами основного конструктора могут быть записаны в одну строку:
class Person(id: Int, name: String)
Классы с более длинными заголовками должны быть отформатированы таким образом, чтобы каждый параметр основного конструктора находился в отдельной строке с отступом. Кроме того, закрывающая скобка должна находиться в новой строке. Если вы используете наследование, вызов конструктора суперкласса или список реализованных интерфейсов должны располагаться в той же строке, что и скобка:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name) { /*...*/ }
Для нескольких интерфейсов, вызов конструктора суперкласса должен располагаться первым, а затем каждый интерфейс — в отдельной строке:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name),
KotlinMaker { /*...*/ }
Для классов с длинным списком супертипов, поставьте перевод строки после двоеточия и выровняйте все имена супертипов по горизонтали:
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne {
fun foo() { /*...*/ }
}
Чтобы четко разделить заголовок класса и тело при длинном заголовке класса, либо добавьте пустую строку после заголовка класса (как в примере выше), либо поместите открывающую фигурную скобку в отдельной строке:
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne
{
fun foo() { /*...*/ }
}
Используйте стандартный отступ (четыре пробела) для параметров конструктора. Это гарантирует, что свойства, объявленные в основном конструкторе, имеют тот же отступ, что и свойства, объявленные в теле класса.
Порядок модификаторов
Если объявление имеет несколько модификаторов, всегда размещайте их в следующем порядке:
public / protected / private / internal expect / actual final / open / abstract / sealed / const external override lateinit tailrec vararg suspend inner enum / annotation / fun // as a modifier in `fun interface` companion inline / value infix operator data
Размещайте все аннотации перед модификаторами:
@Named("Foo")
private val foo: Foo
Если вы не работаете над библиотекой, опускайте избыточные модификаторы (например, public).
Аннотации
Размещайте аннотации в отдельных строках перед объявлением, к которому они относятся, и с таким же отступом:
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
Аннотации без аргументов можно размещать в одной строке:
@JsonExclude @JvmField var x: String
Единственная аннотация без аргументов может быть размещена в одной строке с соответствующим объявлением:
@Test fun foo() { /*...*/ }
Аннотации файлов
Аннотации файлов размещаются после комментария к файлу (если таковой имеется), перед инструкцией package, и отделяются от package пустой строкой (чтобы подчеркнуть тот факт, что они относятся к файлу, а не к пакету).
/** License, copyright and whatever */
@file:JvmName("FooBar")
package foo.bar
Функции
Если подпись функции не помещается в одну строку, используйте следующий синтаксис:
fun longMethodName(
argument: ArgumentType = defaultValue,
argument2: AnotherArgumentType,
): ReturnType {
// body
}
Используйте стандартный отступ (четыре пробела) для параметров функции. Это помогает обеспечить согласованность с параметрами конструктора.
Предпочитается использовать тело-выражение для функций с телом, состоящим из одного выражения.
fun foo(): Int { // bad
return 1
}
fun foo() = 1 // good
Тела-выражения
Если функция имеет тело-выражение, первая строка которого не помещается в одну строку с объявлением, поместите символ = в первую строку и отступите тело-выражение на четыре пробела.
fun f(x: String, y: String, z: String) =
veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)
Свойства
Для очень простых свойств только для чтения рассмотрите форматирование в одну строку:
val isEmpty: Boolean get() = size == 0
Для более сложных свойств всегда ставьте ключевые слова get и set в отдельные строки:
val foo: String
get() { /*...*/ }
Для свойств с инициализатором, если инициализатор длинный, добавьте перевод строки после символа = и отступите инициализатор на четыре пробела:
private val defaultCharset: Charset? =
EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
Инструкции управления потоком
Если условие инструкции if или when многострочное, всегда используйте фигурные скобки вокруг тела инструкции. Отступите каждую последующую строку условия на четыре пробела относительно начала инструкции. Поместите закрывающую скобку условия вместе с открывающей фигурной скобкой в отдельной строке:
if (!component.isSyncing &&
!hasAnyKotlinRuntimeInScope(module)
) {
return createKotlinNotConfiguredPanel(module)
}
Это помогает выровнять условие и блоки инструкций.
Поместите ключевые слова else, catch, finally, а также ключевое слово while цикла do-while в той же строке, что и предшествующая фигурная скобка:
if (condition) {
// body
} else {
// else part
}
try {
// body
} finally {
// cleanup
}
В инструкции when, если ветвь состоит из более чем одной строки, рассмотрите разделение её от соседних блоков case пустой строкой:
private fun parsePropertyValue(propName: String, token: Token) {
when (token) {
is Token.ValueToken ->
callback.visitValue(propName, token.value)
Token.LBRACE -> { // ...
}
}
}
Короткие ветви помещайте в одной строке с условием, без фигурных скобок.
when (foo) {
true -> bar() // good
false -> { baz() } // bad
}
Вызовы методов
В длинных списках аргументов поставьте перевод строки после открывающей скобки. Отступайте аргументы на четыре пробела. Сгруппируйте несколько тесно связанных аргументов в одной строке.
drawSquare(
x = 10, y = 10,
width = 100, height = 100,
fill = true
)
Поставьте пробелы вокруг знака = разделяющего имя аргумента и значение.
Упаковывать цепочки вызовов
При упаковке цепочек вызовов, поместите символ . или оператор ?. в следующей строке с одиночным отступом:
val anchor = owner
?.firstChild!!
.siblings(forward = true)
.dropWhile { it is PsiComment || it is PsiWhiteSpace }
Первый вызов в цепочке обычно должен иметь перевод строки перед ним, но это нормально опустить его, если код приобретает более осмысленный вид без этого.
Лямбды
В выражениях лямбда пробелы должны использоваться вокруг фигурных скобок, а также вокруг стрелки, которая разделяет параметры и тело. Если вызов принимает одну лямбду, передайте её вне скобок, когда это возможно.
list.filter { it > 10 }
Если вы назначаете метку для лямбды, не ставьте пробел между меткой и открывающей фигурной скобкой:
fun foo() {
ints.forEach lit@{
// ...
}
}
При объявлении имён параметров в многострочной лямбде, поместите имена в первой строке, за которыми следует стрелка и перевод строки:
appendCommaSeparated(properties) { prop ->
val propertyValue = prop.get(obj) // ...
}
Если список параметров слишком длинный, чтобы поместиться в строку, поместите стрелку в отдельной строке:
foo {
context: Context,
environment: Env
->
context.configureEnv(environment)
}
Запятые в конце
Запятая в конце — это символ запятой после последнего элемента в ряду элементов:
class Person(
val firstName: String,
val lastName: String,
val age: Int, // trailing comma
)
Использование запятых в конце имеет несколько преимуществ:
Это делает различия в системах управления версиями более чистыми — так как все внимание фокусируется на изменённом значении.
Это упрощает добавление и перестановку элементов — нет необходимости добавлять или удалять запятую, если вы изменяете элементы.
Это упрощает генерацию кода, например, для инициализаторов объектов. Последний элемент также может иметь запятую.
Запятые в конце являются полностью необязательными — ваш код всё равно будет работать без них. Руководство по стилю Kotlin рекомендует использовать запятые в конце при объявлении и оставляет это на ваше усмотрение в местах вызова.
Чтобы включить запятые в конце в формате IntelliJ IDEA, перейдите в Настройки/Preferences | Editor | Стиль кода | Kotlin, откройте вкладку Другие и выберите параметр Использовать запятую в конце.
Перечисления
enum class Direction {
NORTH,
SOUTH,
WEST,
EAST, // trailing comma
}
Аргументы-значения
fun shift(x: Int, y: Int) { /*...*/ }
shift(
25,
20, // trailing comma
)
val colors = listOf(
"red",
"green",
"blue", // trailing comma
)
Свойства и параметры класса
class Customer(
val name: String,
val lastName: String, // trailing comma
)
class Customer(
val name: String,
lastName: String, // trailing comma
)
Параметры значений функций
fun powerOf(
number: Int,
exponent: Int, // trailing comma
) { /*...*/ }
constructor(
x: Comparable<Number>,
y: Iterable<Number>, // trailing comma
) {}
fun print(
vararg quantity: Int,
description: String, // trailing comma
) {}
Параметры с необязательным типом (включая сеттеры)
val sum: (Int, Int, Int) -> Int = fun(
x,
y,
z, // trailing comma
): Int {
return x + y + x
}
println(sum(8, 8, 8))
Индексный суффикс
class Surface {
operator fun get(x: Int, y: Int) = 2 * x + 4 * y - 10
}
fun getZValue(mySurface: Surface, xValue: Int, yValue: Int) =
mySurface[
xValue,
yValue, // trailing comma
]
Параметры в лямбда-выражениях
fun main() {
val x = {
x: Comparable<Number>,
y: Iterable<Number>, // trailing comma
->
println("1")
}
println(x)
}
Элемент when
fun isReferenceApplicable(myReference: KClass<*>) = when (myReference) {
Comparable::class,
Iterable::class,
String::class, // trailing comma
-> true
else -> false
}
Литералы коллекций (в аннотациях)
annotation class ApplicableFor(val services: Array<String>)
@ApplicableFor([
"serializer",
"balancer",
"database",
"inMemoryCache", // trailing comma
])
fun run() {}
Типовые аргументы
fun <T1, T2> foo() {}
fun main() {
foo<
Comparable<Number>,
Iterable<Number>, // trailing comma
>()
}
Типовые параметры
class MyMap<
MyKey,
MyValue, // trailing comma
> {}
Распределённые объявления
data class Car(val manufacturer: String, val model: String, val year: Int)
val myCar = Car("Tesla", "Y", 2019)
val (
manufacturer,
model,
year, // trailing comma
) = myCar
val cars = listOf<Car>()
fun printMeanValue() {
var meanValue: Int = 0
for ((
_,
_,
year, // trailing comma
) in cars) {
meanValue += year
}
println(meanValue/cars.size)
}
printMeanValue()
Комментарии к документации
Для более длинных комментариев к документации поместите открывающую /** на отдельной строке и начинайте каждую последующую строку со звёздочки:
/** * This is a documentation comment * on multiple lines. */
Короткие комментарии можно поместить на одной строке:
/** This is a short documentation comment. */
В целом, избегайте использования @param и @return тегов. Вместо этого, включайте описание параметров и возвращаемых значений непосредственно в комментарий к документации и добавляйте ссылки на параметры, где они упоминаются. Используйте @param и @return только в том случае, если требуется длинное описание, которое не помещается в основной текст.
// Avoid doing this:
/**
* Returns the absolute value of the given number.
* @param number The number to return the absolute value for.
* @return The absolute value.
*/
fun abs(number: Int): Int { /*...*/ }
// Do this instead:
/**
* Returns the absolute value of the given [number].
*/
fun abs(number: Int): Int { /*...*/ }
Избегайте избыточных конструкций
В целом, если определённая синтаксическая конструкция в Kotlin является необязательной и выделена IDE как избыточная, вы должны опустить её в своём коде. Не оставляйте в коде ненужных синтаксических элементов только «для ясности».
Тип возврата Unit
Если функция возвращает Unit, тип возврата следует опустить:
fun foo() { // ": Unit" is omitted here
}
Точки с запятой
Опускайте точки с запятой, когда это возможно.
Шаблоны строк
Не используйте фигурные скобки при вставке простой переменной в шаблон строки. Используйте фигурные скобки только для более длинных выражений.
println("$name has ${children.size} children")
Идиоматическое использование языковых функций
Неизменяемость
Предпочитайте использовать неизменяемые данные, а не изменяемые. Всегда объявляйте локальные переменные и свойства как val вместо var, если они не изменяются после инициализации.
Всегда используйте неизменяемые интерфейсы коллекций (Collection, List, Set, Map ), чтобы объявлять коллекции, которые не изменяются. При использовании функций-фабрик для создания экземпляров коллекций, всегда используйте функции, возвращающие неизменяемые типы коллекций, когда это возможно:
// Bad: use of mutable collection type for value which will not be mutated
fun validateValue(actualValue: String, allowedValues: HashSet<String>) { ... }
// Good: immutable collection type used instead
fun validateValue(actualValue: String, allowedValues: Set<String>) { ... }
// Bad: arrayListOf() returns ArrayList<T>, which is a mutable collection type
val allowedValues = arrayListOf("a", "b", "c")
// Good: listOf() returns List<T>
val allowedValues = listOf("a", "b", "c")
Значения параметров по умолчанию
Предпочитайте объявлять функции со значениями параметров по умолчанию вместо объявления перегруженных функций.
// Bad
fun foo() = foo("a")
fun foo(a: String) { /*...*/ }
// Good
fun foo(a: String = "a") { /*...*/ }
Псевдонимы типов
Если у вас есть функциональный тип или тип с параметрами типа, который используется несколько раз в кодовой базе, предпочтительнее определить для него псевдоним типа:
typealias MouseClickHandler = (Any, MouseEvent) -> Unit typealias PersonIndex = Map<String, Person>
Если вы используете частный или внутренний псевдоним типа для предотвращения столкновения имен, предпочтительнее использовать import … as … , упомянутый в Разделах и импортах.
Параметры лямбда-выражений
В лямбда-выражениях, которые короткие и не вложенные, рекомендуется использовать it соглашение вместо явного объявления параметра. В вложенных лямбда-выражениях с параметрами всегда явно объявляйте параметры.
Возвращаемые значения в лямбда-выражениях
Избегайте использования нескольких меток возврата в лямбда-выражениях. Подумайте о реструктуризации лямбда-выражения так, чтобы у него был единственный выходной пункт. Если это невозможно или недостаточно ясно, рассмотрите возможность преобразования лямбда-выражения в анонимную функцию.
Не используйте метку возврата для последнего оператора в лямбда-выражении.
Имена аргументов
Используйте синтаксис именованных аргументов, когда метод принимает несколько параметров одного примитивного типа или для параметров типа Boolean, если значение всех параметров не очевидно из контекста.
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
Условные операторы
Предпочитайте использовать выразительную форму try, if, и when.
return if (x) foo() else bar()
return when(x) {
0 -> "zero"
else -> "nonzero"
}
Это предпочтительнее, чем:
if (x)
return foo()
else
return bar()
when(x) {
0 -> return "zero"
else -> return "nonzero"
}
if и when
Предпочитайте использовать if для бинарных условий вместо when. Например, используйте этот синтаксис с if.
if (x == null) ... else ...
Вместо этого синтаксиса с when.
when (x) {
null -> // ...
else -> // ...
}
Предпочитайте использовать when если есть три или более вариантов.
Значения nullable Boolean в условиях
Если вам нужно использовать nullable Boolean в условном операторе, используйте if (value == true) или if (value == false) проверки.
Циклы
Предпочитайте использовать функции высшего порядка (filter, map и т.д.) вместо циклов. Исключение: forEach (предпочтительнее использовать обычный for цикл вместо этого, если получатель forEach является nullable или forEach используется как часть более длинной цепочки вызовов).
При выборе между сложным выражением, использующим несколько функций высшего порядка, и циклом, учитывайте стоимость выполняемых операций в каждом случае и имейте в виду соображения производительности.
Циклы по диапазонам
Используйте функцию until для цикла по открытому диапазону:
for (i in 0..n - 1) { /*...*/ } // bad
for (i in 0 until n) { /*...*/ } // good
Строки
Предпочитайте шаблоны строк конкатенации строк.
Предпочитайте многострочные строки встраиванию \n последовательностей в обычные строковые литералы.
Для сохранения отступов в многострочных строках используйте trimIndent если результирующая строка не требует внутренних отступов, или trimMargin , если требуются внутренние отступы:
fun main() {
//sampleStart
println("""
Not
trimmed
text
"""
)
println("""
Trimmed
text
""".trimIndent()
)
println()
val a = """Trimmed to margin text:
|if(a > 1) {
| return a
|}""".trimMargin()
println(a)
//sampleEnd
}
Узнайте разницу между Java и Kotlin многострочными строками.
Функции и свойства
В некоторых случаях функции без аргументов могут быть взаимозаменяемы с только для чтения свойствами. Несмотря на схожую семантику, существуют определённые стилистические соглашения относительно того, что предпочесть.
Предпочитайте свойство функции, когда базовый алгоритм:
не генерирует исключений
легко вычисляется (или кэшируется при первом запуске)
возвращает один и тот же результат при повторных вызовах, если состояние объекта не изменилось
Расширяющие функции
Используйте расширяющие функции широко. Каждый раз, когда у вас есть функция, которая работает в основном с объектом, подумайте о том, чтобы сделать её расширяющей функцией, принимающей этот объект в качестве получателя. Для минимизации загрязнения 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:
fun apiCall(): String = MyJavaApi.getProperty("name")
Любое свойство (на уровне пакета или класса), инициализированное выражением типа платформы, должно явно объявлять свой тип Kotlin:
class Person {
val name: String = MyJavaApi.getProperty("name")
}
Локальная переменная, инициализированная выражением типа платформы, может или не может иметь объявление типа:
fun main() {
val name = MyJavaApi.getProperty("name")
println(name)
}
Функции области apply/with/run/also/let
Kotlin предоставляет набор функций для выполнения блока кода в контексте заданного объекта: let, run, with, apply, и also. Для получения руководства по выбору правильной функции области для вашего случая обратитесь к Функциям области.
Рекомендации по кодированию для библиотек
При написании библиотек рекомендуется следовать дополнительному набору правил, чтобы обеспечить стабильность API:
Всегда явно указывайте видимость членов (чтобы случайно не сделать объявления общедоступным API)
Всегда явно указывайте типы возврата функций и типы свойств (чтобы случайно не изменить тип возврата при изменении реализации)
Предоставляйте KDoc комментарии для всех общедоступных членов, за исключением переопределений, которые не требуют новой документации (чтобы поддерживать генерацию документации для библиотеки)
© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/coding-conventions.html