Spec-Zone.ru › Kotlin 1.4

Рекомендации по стилю кодирования

На этой странице содержатся текущие рекомендации по стилю кодирования для языка Kotlin.

  • Организация исходного кода
  • Правила именования
  • Форматирование
  • Комментарии к документации
  • Избегание избыточных конструкций
  • Идиоматическое использование функций языка
  • Рекомендации по стилю кодирования для библиотек

Применение руководства по стилю

Чтобы настроить форматировщик IntelliJ в соответствии с этим руководством по стилю, пожалуйста, установите плагин Kotlin версии 1.2.20 или новее, перейдите к Настройки | Редактор | Стиль кода | Kotlin, нажмите на ссылку Установить из… в правом верхнем углу и выберите Руководство по стилю Kotlin из меню.

Чтобы убедиться, что ваш код отформатирован в соответствии с руководством по стилю, перейдите к Настройки | Редактор | Проверки и включите проверку Kotlin | Вопросы стиля | Файл не отформатирован в соответствии с настройками проекта. Дополнительные проверки, которые проверяют другие вопросы, описанные в руководстве по стилю (например, правила именования), включены по умолчанию.

Организация исходного кода

Структура каталогов

В чистых проектах Kotlin рекомендуемая структура каталогов соответствует структуре пакетов с пропущенным общим корневым пакетом. Например, если весь код в проекте находится в пакете org.example.kotlin и его подпакетах, файлы с пакетом org.example.kotlin должны быть размещены непосредственно в корневом каталоге исходного кода, а файлы в org.example.kotlin.network.socket должны быть в подкаталоге network/socket корневого каталога исходного кода.

В JVM: В проектах, где Kotlin используется вместе с Java, файлы исходного кода Kotlin должны располагаться в том же корневом каталоге, что и файлы исходного кода Java, и следовать той же структуре каталогов: каждый файл должен храниться в каталоге, соответствующем каждой инструкции пакета.

Имена файлов исходного кода

Если файл Kotlin содержит один класс (возможно, с связанными объявлениями верхнего уровня), его имя должно совпадать с именем класса с добавлением расширения .kt. Если файл содержит несколько классов или только объявления верхнего уровня, выберите имя, описывающее содержимое файла, и дайте файлу соответствующее имя. Используйте верблюжий регистр с заглавной первой буквой (например, ProcessDeclarations.kt).

Имя файла должно описывать то, что делает код в файле. Поэтому следует избегать использования бессмысленных слов, таких как "Util", в именах файлов.

Организация файлов исходного кода

Размещение нескольких объявлений (классов, функций или свойств верхнего уровня) в одном файле Kotlin приветствуется, если эти объявления тесно связаны друг с другом семантически и размер файла остается разумным (не превышает нескольких сотен строк).

В частности, при определении расширяющих функций для класса, которые актуальны для всех клиентов этого класса, поместите их в тот же файл, где определен сам класс. При определении расширяющих функций, которые имеют смысл только для конкретного клиента, поместите их рядом с кодом этого клиента. Не создавайте файлы только для хранения «всех расширений Foo».

Структура класса

В общем случае содержимое класса упорядочивается следующим образом:

  • Объявления свойств и инициализаторы блоков
  • Вторичные конструкторы
  • Объявления методов
  • Объект компаньона

Не сортируйте объявления методов по алфавиту или по видимости и не отделяйте обычные методы от расширяющих методов. Вместо этого объединяйте связанные элементы, чтобы человек, читающий класс сверху вниз, мог следовать логике происходящего. Выберите порядок (сначала более высокие уровни или наоборот) и придерживайтесь его.

Поместите вложенные классы рядом с кодом, который использует эти классы. Если классы предназначены для внешнего использования и не ссылаются внутри класса, поместите их в конец, после объекта компаньона.

Структура реализации интерфейса

При реализации интерфейса сохраняйте реализующие члены в том же порядке, что и члены интерфейса (при необходимости, чередуя их с дополнительными частными методами, используемыми для реализации).

Структура перегрузок

Всегда размещайте перегрузки рядом друг с другом в классе.

Правила именования

Правила именования пакетов и классов в 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 функции, которые хранят глубоко неизменяемые данные) должны использовать имена с заглавными буквами, разделённые символом нижнего подчеркивания:

const val MAX_COUNT = 8
val USER_NAME_FIELD = "UserName"

Имена свойств верхнего уровня или объекта, которые хранят объекты с поведением или изменяемыми данными, должны использовать имена в верблюжьем регистре:

val mutableCollection: MutableSet<String> = HashSet()

Имена свойств, хранящих ссылки на объекты-синглтоны, могут использовать тот же стиль именования, что и объявления object:

val PersonComparator: Comparator<Person> = /*...*/

Для констант перечислений допускается использовать либо имена с заглавными буквами и символом нижнего подчеркивания (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) {
        // ...
    }
}

В Kotlin точки с запятой необязательны, поэтому разрывы строк имеют значение. Дизайн языка предполагает фигурные скобки в стиле Java, и вы можете столкнуться с неожиданным поведением, если вы попробуете использовать другой стиль форматирования.

Горизонтальные пробелы

Размещайте пробелы вокруг бинарных операторов (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

Не ставьте пробел перед ?, используемым для обозначения nullable типа: 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
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
}

Используйте стандартный отступ (4 пробела) для параметров функции.

Обоснование: Согласованность с параметрами конструктора

Предпочитайте использование тела выражения для функций с телом, состоящим из одного выражения.

fun foo(): Int {     // bad
    return 1 
}

fun foo() = 1        // good

Форматирование тела выражения

Если функция имеет тело выражения, которое не помещается в ту же строку, что и объявление, поставьте знак = в первой строке. Отступ тела выражения — четыре пробела.

fun f(x: String) =
    x.length

Форматирование свойств

Для очень простых свойств только для чтения рассмотрите форматирование в одну строку:

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
}

Форматирование вызовов методов

В длинных списках аргументов поставьте перенос строки после открывающей скобки. Отступы аргументов — 4 пробела. Объедините несколько тесно связанных аргументов в одной строке.

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, перейдите в Настройки | Редактор | Стиль кода | Kotlin, откройте вкладку Другие и выберите опцию Использовать запятую после последнего элемента.

Kotlin поддерживает запятую после последнего элемента в следующих случаях:

  • Перечисления
  • Аргументы-значения
  • Свойства и параметры класса
  • Параметры-значения функций
  • Параметры с необязательным типом (включая сеттеры)
  • Индексный суффикс
  • Параметры лямбда-выражений
  • when элемент
  • Коллекции-литералы (в аннотациях)
  • Аргументы типа
  • Параметры типа
  • Деструктурирующие объявления

Перечисления

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) { /*...*/ }

// Do this instead:

/**
 * Returns the absolute value of the given [number].
 */
fun abs(number: 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>

Параметры лямбда-выражений

В лямбда-выражениях, которые короткие и не вложенные, рекомендуется использовать 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. Вместо

when (x) {
    null -> // ...
    else -> // ...
}

используйте if (x == null) ... else ...

Предпочитайте использование when если существует три или более вариантов.

Использование Boolean значений в условиях

Если вам нужно использовать Boolean значение в условном операторе, используйте проверки if (value == true) или if (value == false).

Использование циклов

Предпочитайте использовать функции высшего порядка (filter, map и т.д.) циклам. Исключение: forEach (предпочитайте использовать обычный for цикл вместо этого, если получатель forEach является необязательным или forEach используется как часть более длинной цепочки вызовов).

При выборе между сложным выражением с использованием нескольких функций высшего порядка и циклом, учитывайте стоимость операций в каждом случае и имейте в виду соображения производительности.

Циклы по диапазонам

Используйте функцию until для цикла по открытому диапазону:

for (i in 0..n - 1) { /*...*/ }  // bad
for (i in 0 until n) { /*...*/ }  // good

Использование строк

Предпочитайте использование шаблонов строк конкатенации строк.

Предпочитайте использовать многострочные строки вместо встраивания \n управляющих последовательностей в обычные строковые литералы.

Для сохранения отступов в многострочных строках используйте trimIndent если результирующая строка не требует внутренних отступов или trimMargin если требуются внутренние отступы:

assertEquals(
    """
    Foo
    Bar
    """.trimIndent(), 
    value
)

val a = """if(a > 1) {
          |    return a
          |}""".trimMargin()

Функции против свойств

В некоторых случаях функции без аргументов могут быть взаимозаменяемыми с только для чтения свойствами. Хотя семантика похожа, есть некоторые стилистические соглашения о том, когда предпочесть одно другому.

Предпочитайте свойство функции, когда лежащий в основе алгоритм:

  • не выбрасывает исключения
  • легко вычисляется (или кэшируется при первом запуске)
  • возвращает тот же результат при вызовах, если состояние объекта не изменилось

Использование расширяющих функций

Используйте расширяющие функции свободно. Всякий раз, когда у вас есть функция, которая работает в основном с объектом, рассмотрите возможность превращения её в расширяющую функцию, принимающую этот объект в качестве получателя. Чтобы минимизировать загрязнение API, ограничьте видимость расширяющих функций настолько, насколько это имеет смысл. При необходимости используйте локальные расширяющие функции, расширяющие функции члена или функции расширения верхнего уровня с частной видимостью.

Использование инфиксных функций

Объявляйте функцию как инфиксную только в том случае, если она работает с двумя объектами, которые играют схожую роль. Хорошие примеры: and, to, zip. Плохой пример: add.

END_OF_DOCUMENT_MARKER

Не объявляйте метод как инфиксный, если он изменяет объект-получатель.

Функции-фабрики

Если вы объявляете функцию-фабрику для класса, избегайте давать ей то же имя, что и классу. Предпочитайте использовать отличное имя, ясно указывающее, почему поведение функции-фабрики является особым. Только если нет особой семантики, можно использовать то же имя, что и класс.

Пример:

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–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/coding-conventions.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API