Skip to content
No description, website, or topics provided.
Branch: master
Clone or download
Dmitriy Samaryan
Dmitriy Samaryan added contribution rules
Latest commit 9584138 Nov 27, 2017
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
CONTRIBUTING.md added contribution rules Nov 27, 2017
LICENSE.md added creative commons license Nov 27, 2017
README.md fix headers Nov 27, 2017

README.md

В репозитории приведен набор соглашений по оформлению кода на языке Kotlin.

Этот список правил расширяет предложенные Google и командой разработки Kotlin гайды и пересматривает в них некоторые неоднозначные моменты.

Содержание

  1. Длина строки
  2. Правила именования
  3. Порядок следования модификаторов
  4. Форматирование выражений
  5. Функции
  6. Классы
  7. Аннотации
  8. Структура класса
  9. Форматирование лямбда-выражений
  10. Использование условных операторов
  11. Template header
  12. Файлы

Длина строки

  • Максимальная длина строки: 120 символов.

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

  • Неизменяемые поля в (Companion) Object и compile-time константы именуются в стиле SCREAMING_SNAKE_CASE
  • Для полей View из Kotlin Extension используется стиль lower_snake_case
  • Любые другие поля именуются в стиле lowerCamelCase
  • Функции именуются в стиле lowerCamelCase
  • Классы именуются в стиле UpperCamelCase
  • Пакеты именуются в стиле lower_snake_case

Порядок следования модификаторов

  1. override
  2. public / protected / private / internal
  3. final / open / abstract
  4. const / lateinit
  5. inner
  6. enum / annotation / sealed / data
  7. companion
  8. inline
  9. infix
  10. operator

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

При переносе на новую строку цепочки вызова методов символ . или оператор ?. переносятся на следующую строку, property при этом разрешается оставлять на одной строке:

val collectionItem = source.collectionItems
                ?.dropLast(10)
                ?.sortedBy { it.progress }

Элвис оператор ?: при разрыве выражения также переносится на новую строку:

val promoItemDistanceTradeLink: String = promoItem.distanceTradeLinks?.appLink
            ?: String.EMPTY

При описании переменной с делегатом, не помещающимися на одной строке, оставлять описание с открывающейся фигурной скобкой на одной строке, перенося остальное выражение на следующую строку:

private val promoItem: MarkPromoItem by lazy {
        extractNotNull(BUNDLE_FEED_UNIT_KEY) as MarkPromoItem
}

Функции

Функции с одним выражением

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

Форматирование вызова функции

  • Использование именованного синтаксиса аргументов остается на усмотрение разработчика. Стоит руководствоваться сложностью вызываемого метода: если вызов метода с переданными в него параметрами понятен и очевиден, нет необходимости использовать именованные параметры. При написании именованных аргументов делать перенос каждого аргумента на новую строку с двойным отступом и переносом закрывающейся круглой скобки на следующую строку:
runOperation(
		method = operation::run,
		consumer = consumer,
		errorHandler = errorHandler,
		tag = tag,
		cache = cache,
		cacheMode = cacheMode
)

Форматирование описания функции

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

Вызов переменной функционального типа

  • Всегда использовать полный вариант с написанием invoke у переменной вместо использования сокращенного варианта:
fun runAndCall(expression: () -> Unit): Result {
        val result = run()

        //Bad
        expression()
        //Good
        expression.invoke()

        return result
}

Классы

  • При необходимости разрыва строки осуществляется перенос каждого параметра класса на новую строку с двойным отступом и переносом закрывающейся круглой скобки на следующую строку:
data class CategoryStatistic(
                val id: String,
                val title: String,
                val imageUrl: String,
                val percent: Double
) : Serializable
  • Если в описании класса родительский класс не помещается на одной строке, также осуществляется перенос каждого из его параметров на новую строку с переносом закрывающей круглой скобки на следующую строку.
  • Если описание класса не помещается в одну строку и реализует несколько интерфейсов, то применять стандартные правила переносов, т.е. делать перенос только в случае, когда не помещается на одну строку, и продолжать перечисление интерфейсов на следующей строке.
  • Использование именованного синтаксиса аргументов остается на усмотрение разработчика. Стоит руководствоваться сложностью используемого конструктора класса: если конструктор с переданными в него параметрами понятен и очевиден, нет необходимости использовать именованные параметры.

Аннотации

  • Аннотации как правило располагаются над описанием класса/поля/метода, к которому они применяются.
  • Если к классу/полю/методу есть несколько аннотаций, размещать каждую аннотацию с новой строки:
@JsonValue
@JvmField
var promoItem: PromoItem? = null
  • Если к полю/методу применяется только одна аннотация без параметров, указывать ее над полем/методом.
  • Аннотации, относящиеся к файлу, располагаются сразу после комментария к файлу, и перед package, с разделителем в виде пустой строки.

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

  1. companion object
  2. Поля: abstract, override, public, internal, protected, private
  3. Блок инициализации: init, конструкторы
  4. Абстрактные методы
  5. Переопределенные методы родительского класса(желательно в том же порядке, в каком они следуют в родительском классе)
  6. Реализации методов интерфейсов(желательно в том же порядке, в каком они следуют в описании класса, соблюдая при этом порядок описания этих методов в самом интерфейсе)
  7. public методы
  8. internal методы
  9. protected методы
  10. private методы
  11. inner классы

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

  • При возможности оставлять лямбда-выражение на одной строке, используя it в качестве аргумента.
  • При использовании лямба-функции в качестве аругмента выносить её за скобки если этот параметр единственный.
  • Если выражение возможно написать с передачей метода по ссылке, передавать метод по ссылке (Доступно с 1.1):
viewPager.adapter = QuestAdapter(quest, this::onQuestClicked)
  • При написании лямбда-выражения более чем в одну строку всегда использовать именованный аргумент, вместо it:
viewPager.adapter = QuestAdapter(quest, { quest ->
        onQuestClicked(quest)
})
  • Неиспользуемые параметры лямбда-выражений всегда заменять символом _.
  • Избегать использования Destrucion Declaration в лямбда-выражениях.

Использование условных операторов

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

return if (condition) foo() else bar()

У оператора when для коротких выражениях ветвей условия размещать их на одной строке с условием без фигурных скобок:

when (somenCondition) {
        0 -> fooFunction()
        1 -> barFunction()
        else -> exitFunction()
}

Если хоть в одной из ветвей есть фигурные скобки, обрамлять ими все остальные ветки. У оператора when для блоков с выражениями, которые состоят более чем из одной строки использовать для этих блоков фигурные скобки и отделять смежные case-блоки пустой строкой:

when (feed.type) {
        FeedType.PERSONAL -> {
        	with(feed as PersonalFeed) {
        		datePopupStart = dateBegin
        		datePopupEnd = dateEnd
        	}
        }


        FeedType.SUM -> {
        	with(feed as SumFeed) {
        		datePopupStart = dateBegin
        		datePopupEnd = dateEnd
        	}
        }

        FeedType.CARD -> {
        	with(feed as CardFeed) {
        		datePopupStart = dateBegin
        		datePopupEnd = dateEnd
        	}
        }

        else -> {
        	Feed.EMPTY
        }
}

Template header

  • Не использовать Template Header для классов (касается авторства и даты создания файла).

Файлы

  • Возможно описывать несколько классов в одном файле только для sealed классов. В остальных случаях для каждого класса необходимо использовать отдельный файл (не относится к inner классам).
You can’t perform that action at this time.