3

GitHub - RedMadRobot/kotlin-style-guide: red_mad_robot Kotlin Style Guide

 1 year ago
source link: https://github.com/RedMadRobot/kotlin-style-guide
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Kotlin Code Style

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

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

Длина строки

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

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

  • Пакеты именуются одним словом в стиле lowercase. Если необходимо использовать несколько слов, то просто склеиваем их вместе.

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

  • При переносе на новую строку цепочки вызова методов символ . или оператор ?. переносятся на следующую строку, property при этом разрешается оставлять на одной строке:
val collectionItems = source.collectionItems
    ?.dropLast(10)
    ?.sortedBy { it.progress }
  • Элвис оператор ?: в многострочном выражении также переносится на новую строку:
val throwableMessage: String = throwable?.message
    ?: DEFAULT_ERROR_MESSAGE

throwable.message?.let { showError(it) }
    ?: showError(DEFAULT_ERROR_MESSAGE)
  • Если перед элвис оператором ?: многострочная лямбда, желательно перенести также и лямбду:
// Good
throwable.message
    ?.let { message ->
        ...
        showError(message)
    }
    ?: showError(DEFAULT_ERROR_MESSAGE)
    
// Not recommended
throwable.message?.let { message ->
    ...
    showError(message)
}
    ?: showError(DEFAULT_ERROR_MESSAGE)
  • При описании переменной с делегатом, не помещающимися на одной строке, оставлять описание с открывающейся фигурной скобкой на одной строке, перенося остальное выражение на следующую строку:
private val promoItem: MarkPromoItem by lazy {
    extractNotNull(BUNDLE_FEED_UNIT_KEY) as MarkPromoItem
}

Функции

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

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

Именованные аргументы

  • Если по контексту не понятно назначение аргумента, то следует сделать его именованным.
runOperation(
    method = operation::run,
    consumer,
    errorHandler,
    tag,
    cacheSize = 3,
    cacheMode
)
calculateSquare(x = 6, y = 19)
getCurrentUser(skipCache = false)
setProgressBarVisible(true)
  • Если именованные аргументы не помещаются на одной строке, то следует переносить каждый аргумент на новую строку (как в примере выше).
  • Именуем все лямбды, принимаемые функцией в качестве аргументов (кроме случаев когда лямбда вынесена за круглые скобки), чтобы во время чтения кода было понятно назначение и ответственность каждой лямбды.
editText.addTextChangedListener(
    onTextChanged = { text, _, _, _ -> 
        viewModel.onTextChanged(text?.toString())
    },
    afterTextChanged = { text ->
        viewModel.onAfterTextChanged(text?.toString())
    }
)
  • Полезно именовать аргументы одинаковых типов, чтобы случайно не перепутать их местами.
val startDate: Date = ..
val endDate: Date = ..
compareDates(startDate = startDate, endDate = endDate)
  • Полезно именовать аргумент при передаче null.
setAdditionalArguments(arguments = null)

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

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

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

  • По возможности передавать метод по ссылке:
viewPager.adapter = QuestAdapter(quest, onQuestClickListener = ::onQuestClicked)
  • При написании лямбда-выражения более чем в одну строку всегда использовать именованный аргумент, вместо it:
viewPager.adapter = QuestAdapter(
    quest, 
    onQuestClickListener = { quest ->
        Log.d(..)
        viewModel.onQuestClicked(quest)
    }
)
  • Неиспользуемые параметры лямбда-выражений всегда заменять символом _.

Классы

  • Если описание класса не помещается в одну строку, и класс реализует несколько интерфейсов, то применять стандартные правила переноса, т.е. делать перенос только в случае, когда описание не помещается на одну строку, при этом продолжать перечисление интерфейсов на следующей строке.
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne,
    OneMoreVeryLongInteface, OneMore{

    fun foo() { /*...*/ }
}

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

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

Аннотации

  • Аннотации располагаются над описанием класса/поля/метода, к которому они применяются.
  • Если к классу/полю/методу применяется несколько аннотаций, размещать каждую аннотацию с новой строки:
@JsonValue
@JvmField
var promoItem: PromoItem? = null
  • Аннотации к аргументам в конструкторе класса или объявлении функции можно писать на той же строке, что и соответствующий аргумент.
    При этом если аннотаций к одному аргументу несколько, то все аннотации пишутся с новой строки, и соответствующий аргумент отделяется от других сверху и снизу пустыми строками.
data class UserInfo (
    @SerializedName("firstName") val firstName: String? = null,
    @SerializedName("secondName") val secondName: String? = null
)

@Entity(tableName = "users")
data class UserInfo (
    @PrimaryKey val id: Int,
    
    @SerializedName("firstName") 
    @ColumnInfo(name = "firstName") 
    val firstName: String? = null,
    
    @SerializedName("secondName") 
    @ColumnInfo(name = "secondName") 
    val secondName: String? = null
)

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

  • Не обрамлять if выражения в фигурные скобки только если условный оператор if помещается в одну строку.
    По возможности использовать условные операторы, как выражение:
return if (condition) foo() else bar()
  • В операторе when ветки, состоящие более чем из одной строки, обрамлять фигурными скобками и отделять от других case-веток пустыми строками сверху и снизу.
when (feed.type) {
    FeedType.PERSONAL -> startPersonalFeedScreen()
    
    FeedType.SUM -> {
        showSumLayout()
        hideProgressBar()
    }
    
    FeedType.CARD -> startCardFeedScreen()
    else -> showError() 
}

Template header

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

Частые ошибки

Вызов toString() у nullable объектов

  • В первом примере получится строчка "null", это плохо. Необходимо сделать так, чтобы в таком случае возвращалась пустая строка ""
binding.authInputPassword.addTextChangeListener { editable: Editable? ->
    // Bad
    viewModel.onPasswordChanged(editable.toString())
    
    // Good
    viewModel.onPasswordChanged(editable?.toString().orEmpty())
}

Использование orEmpty() вместо ?:

  • Для коллекций и строк использовать orEmpty().
// Bad
nullableString ?: ""
nullableObject?.toString() ?: ""
someList ?: emptyList()

// Good
nullableString.orEmpty()
nullableObject?.toString().orEmpty()
someList.orEmpty()

Проверка nullable boolean

  • При проверке nullable boolean вместо добавления ?: false в условии явно проверять boolean == true
    Это одна из общепринятных идиом Kotlin.
// Bad
val b: Boolean? = ...
if (boolean ?: false) {
    ...
} else {
    // `b` is false or null
}

// Good
val b: Boolean? = ...
if (b == true) {
    ...
} else {
    // `b` is false or null
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK