Skip to content

Latest commit

 

History

History
251 lines (182 loc) · 5.85 KB

CODESTYLE.md

File metadata and controls

251 lines (182 loc) · 5.85 KB

Кotlin

Based on Kotlin Coding Conventions

This document supplements and also redefines certain provisions of these standards. In case of conflict follow this document.

General points

  1. The principle of consistency should be used: new (or fixed) code should follow the style of the code written around it. Since different variations of text layout are allowed, it is necessary to adhere to the same approach and style within the same file.
  2. You should not use Java-familiar approaches when alternative Kotlin-style approaches are available. In other words, don't try to write Kotlin like Java.

Naming

Names of classes, fields, variables and parameters should be chosen from English words (transliteration is not allowed) or established abbreviations (abbreviations should be used as words). Unknown abbreviations should not be used.

Good Bad
Separator Razdelitel
XmlHttpRequest XMLHTTPRequest
getId getID
class Html class HTML
url: String URL: String
id: Long ID: Long
JustOneNewAcro JONA

Imports

Each import statement must be written on one line without breaks. The list of expressions should be sorted alphabetically and should not contain unused imports. Wildcards are forbidden.

Formatting

Line length

Maximum line length - 120 characters.

Vertical indentation

Methods must be separated from each other by one empty line.

Curly braces

Don't use curly braces in single-line if statement. In multiline if statements curly braces must be used.

Good:

    val myVal = if (condition) 1 else 0
    if (condition) doSomething()
    if (condition1) {
        doSomething1()
        doSomething2()
        doSomething3()
    }

    if (condition2) {
        value = Value.builder()
            .someThing1(someThing1)
            .someThing2(someThing2)
            .someThing3(someThing3)
            .build()
    }

In methods with an empty implementation, curly braces should be replaced with the Unit expression.

Good:

    override fun empty() = Unit

Bad:

    override fun empty() {
    }

Expression bodies

Always use expression body of functions in case of trivial expressions.

Good:

    fun sum(a: Int, b: Int) = a + b

Bad:

    fun sum(a: Int, b: Int): Int { 
        return a + b
    }

For more complex expressions, you can use the expression body in case of single-line statement, including the situation with line wrapping to the next 1 (according to the rules for wrapping long statements)

Good:

    // 1 line
    fun doPerform() = actionFactory.createPerformer.perform()
    // 1 line with a break
    fun dispatchNextState(mode: CaptureMode = null): Unit =
        fallbackStateDispatcher.dispatchByMode(captureMode = mode ?: CaptureMode.DEFAULT, oldState = currentState)

Bad:

    fun doEither(condition: Boolean): Result = 
        if (condition) {
            myPerformer.doActual()
        } else {
            Result.FAIL
        }
    fun doEither(condition: Boolean?): Result = 
        when (condition) {
            true -> Result.SUCCESS
            null -> Result.UNKNOWN
            false -> Result.FAIL
        }

Return types

Declare type explicitly if:

  • function is a part of public api
  • it's not clear what will be returned
  • should pay attention to the type
  • platform type is used

Good:

    fun method(a: Type1, b: Type2): Type3 = a.doA(b.getB())
    fun sum(a: Int, b: Int): Float = а * b.toFloat()
    fun toast(text: String): Toast = Toast.makeText(activity, text, Toast.LENGTH_LONG)

Bad:

    fun method(a: Type1, b: Type2) = a.doA(b.getB())
    fun sum(a: Int, b: Int) = а * b.toFloat()
    fun toast(text: String) = Toast.makeText(activity, text, Toast.LENGTH_LONG)

Don't specify return type if it is obvious.

Good:

    fun sum(a: Int, b: Int) = a + b

Bad:

    fun sum(a: Int, b: Int): Int = а + b

Comments

Comments must be written only in English.

Idiomatic use of language features

public lateinit

Don't use lateinit modifier for public properties.

Bad:

    lateinit var someString: String

Nullability

  1. Unsafe operator !! is not allowed.
  2. Elvis operator is preferred.

Good:

    optional?.doSomething()

Bad:

    if (optional != null) {
        optional.doSomething()
    }
    optional!!.doSomething()

Named arguments

Use the named argument syntax for complex functions with many arguments. If the arguments contain Boolean and/or primitives, the use of a named call is mandatory. Use of positional parameters only if their meaning is absolutely clear from the context.

Good:

drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)

Extension functions

Use extension functions instead of utility functions.

Good:

fun String.removeFirstLastChar() = substring(1, length - 1)

Bad:

fun removeFirstLastChar(string: String) = string.substring(1, string.length - 1)

Enum vs @IntDef/@StringDef

In all cases, the use of enum class or sealed class is recommended instead of @IntDef/@StringDef annotations.

Scope functions apply/with/run/also/let

It is recommended to:

  1. Use apply to set up or initialize an object and avoid using it for logic.
  2. Don't overuse this API and try to keep it readable in natural English style.
  3. Avoid excessive nesting.
  4. Do not use nested with/run/apply. In case of nested also/let, naming of the argument is required (you can't use it).