An introduction to kotlin coding conventions

Ali Ahmadabadiha

An introduction to kotlin coding conventions

Placing multiple declarations (classes, top-level functions or properties) in the same Kotlin source file is encouraged as long as these declarations are closely related to each other semantically, and the file size remains reasonable (not exceeding a few hundred lines).

Source code organization

In particular, when defining extension functions for a class which are relevant for all clients of this class, put them in the same file with the class itself. When defining extension functions that make sense only for a specific client, put them next to the code of that client. Avoid creating files just to hold all extensions of some class.

Source code organization

The contents of a class should go in the following order:

  1. Property declarations and initializer blocks

  2. Secondary constructors

  3. Method declarations

  4. Companion object

Source code organization

Do not sort the method declarations alphabetically or by visibility, and do not separate regular methods from extension methods. Instead, put related stuff together, so that someone reading the class from top to bottom can follow the logic of what's happening.

Source code organization

Put nested classes next to the code that uses those classes. If the classes are intended to be used externally and aren't referenced inside the class, put them in the end, after the companion object.

Source code organization

When implementing an interface, keep the implementing members in the same order as members of the interface (if necessary, interspersed with additional private methods used for the implementation).

Source code organization

Always put overloads next to each other in a class.

Source code organization

Always put overloads next to each other in a class.

Source code organization

Names of packages are always lowercase and do not use underscores (org.example.project). Using multi-word names is generally discouraged, but if you do need to use multiple words, you can either just concatenate them together or use the camel case (org.example.myProject).

Naming rules

Names of functions, properties and local variables start with a lowercase letter and use the camel case and no underscores.

Naming rules

Exception: factory functions used to create instances of classes can have the same name as the abstract return type:

Naming rules

Exception: factory functions used to create instances of classes can have the same name as the abstract return type:

Naming rules

interface Foo { /*...*/ }

class FooImpl : Foo { /*...*/ }

fun Foo(): Foo { return FooImpl() }

In tests (and only in tests), you can use method names with spaces enclosed in backticks. Note that such method names are only supported by Android runtime from API level 30. Underscores in method names are also allowed in test code.

Naming rules

In tests (and only in tests), you can use method names with spaces enclosed in backticks. Note that such method names are only supported by Android runtime from API level 30. Underscores in method names are also allowed in test code.

Naming rules

class MyTestCase {
     @Test fun `ensure everything works`() { /*...*/ }

     @Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}

Names of constants (properties marked with const, or top-level or object val properties with no custom get function that hold deeply immutable data) should use uppercase underscore-separated (screaming snake case) names.

Naming rules

For enum constants, it's OK to use either uppercase underscore-separated names (screaming snake case) (enum class Color { RED, GREEN }) or upper camel case names, depending on the usage.

Naming rules

If a class has two properties which are conceptually the same but one is part of a public API and another is an implementation detail, use an underscore as the prefix for the name of the private property.

Naming rules

If a class has two properties which are conceptually the same but one is part of a public API and another is an implementation detail, use an underscore as the prefix for the name of the private property.

Naming rules

class C {
    private val _elementList = mutableListOf<Element>()

    val elementList: List<Element>
         get() = _elementList
}

The name of a class is usually a noun or a noun phrase explaining what the class is: List, PersonReader.

Naming rules

The name of a method is usually a verb or a verb phrase saying what the method does: close, readPersons. The name should also suggest if the method is mutating the object or returning a new one. For instance sort is sorting a collection in place, while sorted is returning a sorted copy of the collection.

Naming rules

The names should make it clear what the purpose of the entity is, so it's best to avoid using meaningless words (Manager, Wrapper) in names.

Naming rules

When using an acronym as part of a declaration name, capitalize it if it consists of two letters (IOStream); capitalize only the first letter if it is longer (XmlFormatter, HttpInputStream).

Naming rules

Place annotations on separate lines before the declaration to which they are attached, and with the same indentation:

Formatting

Place annotations on separate lines before the declaration to which they are attached, and with the same indentation:

Formatting

@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude

Annotations without arguments may be placed on the same line:

Formatting

Annotations without arguments may be placed on the same line:

Formatting

@JsonExclude @JvmField
var x: String

A single annotation without arguments may be placed on the same line as the corresponding declaration:

Formatting

A single annotation without arguments may be placed on the same line as the corresponding declaration:

Formatting

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

File annotations are placed after the file comment (if any), before the package statement, and are separated from package with a blank line (to emphasize the fact that they target the file and not the package).

Formatting

File annotations are placed after the file comment (if any), before the package statement, and are separated from package with a blank line (to emphasize the fact that they target the file and not the package).

Formatting

/** License, copyright and whatever */
@file:JvmName("FooBar")

package foo.bar

Prefer using an expression body for functions with the body consisting of a single expression.

Formatting

If the function has an expression body whose first line doesn't fit on the same line as the declaration, put the = sign on the first line and indent the expression body by four spaces.

Formatting

If the function has an expression body whose first line doesn't fit on the same line as the declaration, put the = sign on the first line and indent the expression body by four spaces.

Formatting

fun f(x: String, y: String, z: String) =
    veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)

If the condition of an if or when statement is multiline, always use curly braces around the body of the statement. Indent each subsequent line of the condition by four spaces relative to the statement start. Put the closing parentheses of the condition together with the opening curly brace on a separate line:

Formatting

Formatting

if (!component.isSyncing &&
    !hasAnyKotlinRuntimeInScope(module)
) {
    return createKotlinNotConfiguredPanel(module)
}

In a when statement, if a branch is more than a single line, consider separating it from adjacent case blocks with a blank line:

Formatting

In a when statement, if a branch is more than a single line, consider separating it from adjacent case blocks with a blank line:

Formatting

private fun parsePropertyValue(propName: String, token: Token) {
    when (token) {
        is Token.ValueToken ->
            callback.visitValue(propName, token.value)

        Token.LBRACE -> { // ...
        }
    }
}

Put short branches on the same line as the condition, without braces.

Formatting

Put short branches on the same line as the condition, without braces.

Formatting

when (foo) {
    true -> bar() // good
    false -> { baz() } // bad
}

In long argument lists, put a line break after the opening parenthesis. Indent arguments by four spaces. Group multiple closely related arguments on the same line.

Formatting

In long argument lists, put a line break after the opening parenthesis. Indent arguments by four spaces. Group multiple closely related arguments on the same line.

Formatting

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

When wrapping chained calls, put the . character or the ?. operator on the next line, with a single indent:

Formatting

When wrapping chained calls, put the . character or the ?. operator on the next line, with a single indent:

Formatting

val anchor = owner
    ?.firstChild!!
    .siblings(forward = true)
    .dropWhile { it is PsiComment || it is PsiWhiteSpace }

When declaring parameter names in a multiline lambda, put the names on the first line, followed by the arrow and the newline:

Formatting

When declaring parameter names in a multiline lambda, put the names on the first line, followed by the arrow and the newline:

Formatting

appendCommaSeparated(properties) { prop ->
    val propertyValue = prop.get(obj)  // ...
}

If the parameter list is too long to fit on a line, put the arrow on a separate line:

Formatting

If the parameter list is too long to fit on a line, put the arrow on a separate line:

Formatting

foo {
   context: Context,
   environment: Env
   ->
   context.configureEnv(environment)
}

A trailing comma is a comma symbol after the last item in a series of elements:

Formatting

class Person(
    val firstName: String,
    val lastName: String,
    val age: Int, // trailing comma
)

Using trailing commas has several benefits:

  • It makes version-control diffs cleaner – as all the focus is on the changed value.

  • It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements.

  • It simplifies code generation, for example, for object initializers. The last element can also have a comma.

Formatting

Trailing commas are entirely optional – your code will still work without them. The Kotlin style guide encourages the use of trailing commas at the declaration site and leaves it at your discretion for the call site.

Formatting

For longer documentation comments, place the opening /** on a separate line and begin each subsequent line with an asterisk:

Documentation comments

For longer documentation comments, place the opening /** on a separate line and begin each subsequent line with an asterisk:

Documentation comments

/**
 * This is a documentation comment
 * on multiple lines.
 */

Short comments can be placed on a single line:

Documentation comments

/** This is a short documentation comment. */

Generally, avoid using @param and @return tags. Instead, incorporate the description of parameters and return values directly into the documentation comment, and add links to parameters wherever they are mentioned. Use @param and @return only when a lengthy description is required which doesn't fit into the flow of the main text.

Documentation comments

Documentation comments

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

Prefer using immutable data to mutable. Always declare local variables and properties as val rather than var if they are not modified after initialization.

Idiomatic use of language features

Always use immutable collection interfaces (Collection, List, Set, Map) to declare collections which are not mutated. When using factory functions to create collection instances, always use functions that return immutable collection types when possible:

Idiomatic use of language features

Idiomatic use of language features

// Bad: use of a 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")

Prefer declaring functions with default parameter values to declaring overloaded functions.

Idiomatic use of language features

Prefer declaring functions with default parameter values to declaring overloaded functions.

Idiomatic use of language features

// Bad
fun foo() = foo("a")
fun foo(a: String) { /*...*/ }

// Good
fun foo(a: String = "a") { /*...*/ }

In lambdas which are short and not nested, it's recommended to use the it convention instead of declaring the parameter explicitly. In nested lambdas with parameters, always declare parameters explicitly.

Idiomatic use of language features

Avoid using multiple labeled returns in a lambda. Consider restructuring the lambda so that it will have a single exit point. If that's not possible or not clear enough, consider converting the lambda into an anonymous function.

Do not use a labeled return for the last statement in a lambda.

Idiomatic use of language features

Use the named argument syntax when a method takes multiple parameters of the same primitive type, or for parameters of Boolean type, unless the meaning of all parameters is absolutely clear from context.

Idiomatic use of language features

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

If you need to use a nullable Boolean in a conditional statement, use if (value == true) or if (value == false) checks.

Idiomatic use of language features

Prefer using higher-order functions (filter, map etc.) to loops. Exception: forEach (prefer using a regular for loop instead, unless the receiver of forEach is nullable or forEach is used as part of a longer call chain).

Idiomatic use of language features

When making a choice between a complex expression using multiple higher-order functions and a loop, understand the cost of the operations being performed in each case and keep performance considerations in mind.

Idiomatic use of language features

Prefer string templates to string concatenation.

Prefer multiline strings to embedding \n escape sequences into regular string literals.

To maintain indentation in multiline strings, use trimIndent when the resulting string does not require any internal indentation, or trimMargin when internal indentation is required:

Idiomatic use of language features

Idiomatic use of language features

println("""
    Not
    trimmed
    text
    """
       )

println("""
    Trimmed
    text
    """.trimIndent()
       )

println()

val a = """Trimmed to margin text:
          |if(a > 1) {
          |    return a
          |}""".trimMargin()

println(a)

Idiomatic use of language features

println("""
    Not
    trimmed
    text
    """
       )

println("""
    Trimmed
    text
    """.trimIndent()
       )

println()

val a = """Trimmed to margin text:
          |if(a > 1) {
          |    return a
          |}""".trimMargin()

println(a)


    Not
    trimmed
    text
   
Trimmed
text

Trimmed to margin text:
if(a > 1) {
    return a
}

Use extension functions liberally. Every time you have a function that works primarily on an object, consider making it an extension function accepting that object as a receiver. To minimize API pollution, restrict the visibility of extension functions as much as it makes sense. As necessary, use local extension functions, member extension functions, or top-level extension functions with private visibility.

Idiomatic use of language features

If you declare a factory function for a class, avoid giving it the same name as the class itself. Prefer using a distinct name, making it clear why the behavior of the factory function is special. Only if there is really no special semantics, you can use the same name as the class.

Idiomatic use of language features

If you declare a factory function for a class, avoid giving it the same name as the class itself. Prefer using a distinct name, making it clear why the behavior of the factory function is special. Only if there is really no special semantics, you can use the same name as the class.

Idiomatic use of language features

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

If you have an object with multiple overloaded constructors that don't call different superclass constructors and can't be reduced to a single constructor with default argument values, prefer to replace the overloaded constructors with factory functions.

Idiomatic use of language features

When writing libraries, it's recommended to follow an additional set of rules to ensure API stability:

  • Always explicitly specify member visibility (to avoid accidentally exposing declarations as public API)

  • Always explicitly specify function return types and property types (to avoid accidentally changing the return type when the implementation changes)

  • Provide KDoc comments for all public members, except for overrides that do not require any new documentation (to support generating documentation for the library)

Coding conventions for libraries

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Google Android Style

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

A Kotlin source file is described as being in Google Android Style if and only if it adheres to the rules herein.

Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this document focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn’t clearly enforceable (whether by human or tool).

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

If a source file contains only a single top-level class, the file name should reflect the case-sensitive name plus the .kt extension. Otherwise, if a source file contains multiple top-level declarations, choose a name that describes the contents of the file, apply PascalCase (camelCase is acceptable if the filename is plural), and append the .kt extension.

Source files: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Source files: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
// MyClass.kt
class MyClass { }
// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …
// extensions.kt
fun MyClass.process() = // …
fun MyResult.print() = // …

A .kt file comprises the following, in order:

  • Copyright and/or license header (optional)
  • File-level annotations
  • Package statement
  • Import statements
  • Top-level declarations

Exactly one blank line separates each of these sections.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

A .kt file comprises the following, in order:

  • Copyright and/or license header (optional)
  • File-level annotations
  • Package statement
  • Import statements
  • Top-level declarations

Exactly one blank line separates each of these sections.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Annotations with the "file" use-site target are placed between any header comment and the package declaration.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

The package statement is not subject to any column limit and is never line-wrapped.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Import statements for classes, functions, and properties are grouped together in a single list and ASCII sorted.

Wildcard imports (of any type) are not allowed.

Similar to the package statement, import statements are not subject to a column limit and they are never line-wrapped.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

The contents of a file should be focused on a single theme. Examples of this would be a single public type or a set of extension functions performing the same operation on multiple receiver types. Unrelated declarations should be separated into their own files and public declarations within a single file should be minimized.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

No explicit restriction is placed on the number nor order of the contents of a file.

Source files are usually read from top-to-bottom meaning that the order, in general, should reflect that the declarations higher up will inform understanding of those farther down. Different files may choose to order their contents differently. Similarly, one file may contain 100 properties, another 10 functions, and yet another a single class.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

What is important is that each file uses some logical order, which its maintainer could explain if asked. For example, new functions are not just habitually added to the end of the file, as that would yield “chronological by date added” ordering, which is not a logical ordering.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

The order of members within a class follow the same rules as the top-level declarations.

Source files: Structure

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Braces are not required for when branches and if expressions which have no more than one else branch and which fit on a single line.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Braces are not required for when branches and if expressions which have no more than one else branch and which fit on a single line.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
if (string.isEmpty()) return

val result =
    if (string.isEmpty()) DEFAULT_VALUE else string

when (value) {
    0 -> return
    // …
}

Braces are otherwise required for any if, for, when branch, do, and while statements and expressions, even when the body is empty or contains only a single statement.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
if (string.isEmpty())
    return  // WRONG!

if (string.isEmpty()) {
    return  // Okay
}

if (string.isEmpty()) return  // WRONG
else doLotsOfProcessingOn(string, otherParametersHere)

if (string.isEmpty()) {
    return  // Okay
} else {
    doLotsOfProcessingOn(string, otherParametersHere)
}

Braces follow the Kernighan and Ritchie style ("Egyptian brackets") for nonempty blocks and block-like constructs:

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
  • No line break before the opening brace.
  • Line break after the opening brace.
  • Line break before the closing brace.
  • Line break after the closing brace, only if that brace terminates a statement or terminates the body of a function, constructor, or named class. For example, there is no line break after the brace if it is followed by else or a comma.

A few exceptions for enum classes are given later.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
return Runnable {
    while (condition()) {
        foo()
    }
}

return object : MyClass() {
    override fun foo() {
        if (condition()) {
            try {
                something()
            } catch (e: ProblemException) {
                recover()
            }
        } else if (otherCondition()) {
            somethingElse()
        } else {
            lastThing()
        }
    }
}

An empty block or block-like construct must be in K&R style.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
try {
    doSomething()
} catch (e: Exception) {} // WRONG!
try {
    doSomething()
} catch (e: Exception) {
} // Okay

An if/else conditional that is used as an expression may omit braces only if the entire expression fits on one line.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

An if/else conditional that is used as an expression may omit braces only if the entire expression fits on one line.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
val value = if (string.isEmpty()) 0 else 1  // Okay
val value = if (string.isEmpty())  // WRONG!
    0
else
    1
val value = if (string.isEmpty()) { // Okay
    0
} else {
    1
}

Each time a new block or block-like construct is opened, the indent increases by four spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Each statement is followed by a line break. Semicolons are not used.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained below.

Exceptions:

  • Lines where obeying the column limit is not possible (for example, a long URL in KDoc)
  • package and import statements
  • Command lines in a comment that may be cut-and-pasted into a shell

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

The prime directive of line-wrapping is: prefer to break at a higher syntactic level. Also:

  • When a line is broken at an operator or infix function name, the break comes after the operator or infix function name.
  • When a line is broken at the following “operator-like” symbols, the break comes before the symbol:
    • The dot separator (., ?.).
    • The two colons of a member reference (::).

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
  • A method or constructor name stays attached to the open parenthesis (() that follows it.
  • A comma (,) stays attached to the token that precedes it.
  • A lambda arrow (->) stays attached to the argument list that precedes it.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

When a function signature does not fit on a single line, break each parameter declaration onto its own line. Parameters defined in this format should use a single indent (+4). The closing parenthesis ()) and return type are placed on their own line with no additional indent.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = ""
): String {
    // …
}

When a function contains only a single expression it can be represented as an expression function.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
override fun toString(): String {
    return "Hey"
}
override fun toString(): String = "Hey"

 

Properties declaring a get and/or set function should place each on their own line with a normal indent (+4). Format them using the same rules as functions.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
var directory: File? = null
    set(value) {
        // …
    }

 

Read-only properties can use a shorter syntax which fits on a single line.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
val defaultExtension: String get() = "kt"

 

Read-only properties can use a shorter syntax which fits on a single line.

Formatting: Braces

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
val defaultExtension: String get() = "kt"

 

A single blank line appears:

  • Between consecutive members of a class: properties, constructors, functions, nested classes, etc.

Formatting: Whitespace

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
  • Exception: A blank line between two consecutive properties (having no other code between them) is optional. Such blank lines are used as needed to create logical groupings of properties and associate properties with their backing property, if present.
  • Exception: Blank lines between enum constants are covered below.

Formatting: Whitespace

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
  • Between statements, as needed to organize the code into logical subsections.
  • Optionally before the first statement in a function, before the first member of a class, or after the last member of a class (neither encouraged nor discouraged).
  • As required by other sections (such as the Structure section).

Formatting: Whitespace

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
  • Between statements, as needed to organize the code into logical subsections.
  • Optionally before the first statement in a function, before the first member of a class, or after the last member of a class (neither encouraged nor discouraged).
  • As required by other sections (such as the Structure section).

Multiple consecutive blank lines are permitted, but not encouraged or ever required.

Formatting: Whitespace

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

An enum with no functions and no documentation on its constants may optionally be formatted as a single line.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
enum class Answer { YES, NO, MAYBE }

When the constants in an enum are placed on separate lines, a blank line is not required between them except in the case where they define a body.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
enum class Answer {
    YES,
    NO,

    MAYBE {
        override fun toString() = """¯\_(ツ)_/¯"""
    }
}

Member or type annotations are placed on separate lines immediately prior to the annotated construct.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
@Retention(SOURCE)
@Target(FUNCTION, PROPERTY_SETTER, FIELD)
annotation class Global

Annotations without arguments can be placed on a single line.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
@JvmField @Volatile
var disposable: Disposable? = null

When only a single annotation without arguments is present, it may be placed on the same line as the declaration.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
@Volatile var disposable: Disposable? = null

@Test fun selectAll() {
    // …
}

When only a single annotation without arguments is present, it may be placed on the same line as the declaration.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
@Volatile var disposable: Disposable? = null

@Test fun selectAll() {
    // …
}

If an expression function body or a property initializer is a scalar value or the return type can be clearly inferred from the body then it can be omitted.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
override fun toString(): String = "Hey"
// becomes
override fun toString() = "Hey"

When writing a library, retain the explicit type declaration when it is part of the public API.

Formatting: Specific constructs

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Package names are all lowercase, with consecutive words simply concatenated together (no underscores).

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Class names are written in PascalCase and are typically nouns or noun phrases. For example, Character or ImmutableList. Interface names may also be nouns or noun phrases (for example, List), but may sometimes be adjectives or adjective phrases instead (for example Readable).

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Test classes are named starting with the name of the class they are testing, and ending with Test. For example, HashTest or HashIntegrationTest.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Function names are written in camelCase and are typically verbs or verb phrases. For example, sendMessage or stop.

Underscores are permitted to appear in test function names to separate logical components of the name.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Functions annotated with @Composable that return Unit are PascalCased and named as nouns, as if they were types.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Constant names use UPPER_SNAKE_CASE: all uppercase letters, with words separated by underscores. But what is a constant, exactly?

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Constants are val properties with no custom get function, whose contents are deeply immutable, and whose functions have no detectable side-effects. This includes immutable types and immutable collections of immutable types as well as scalars and string if marked as const. If any of an instance’s observable state can change, it is not a constant. Merely intending to never mutate the object is not enough.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}
const val NUMBER = 5
val NAMES = listOf("Alice", "Bob")
val AGES = mapOf("Alice" to 35, "Bob" to 32)
val COMMA_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf

These names are typically nouns or noun phrases.

Constant values can only be defined inside of an object or as a top-level declaration. Values otherwise meeting the requirement of a constant but defined inside of a class must use a non-constant name.

Constants which are scalar values must use the const modifier.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Non-constant names are written in camelCase. These apply to instance properties, local properties, and parameter names.

These names are typically nouns or noun phrases.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

When a backing property is needed, its name should exactly match that of the real property except prefixed with an underscore.

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Each type variable is named in one of two styles:

  • A single capital letter, optionally followed by a single numeral (such as E, T, X, T2)
  • A name in the form used for classes, followed by the capital letter T (such as RequestT, FooBarT)

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

       Prose form                             Correct                                 Incorrect

"XML Http Request" XmlHttpRequest XMLHTTPRequest
"new customer ID" newCustomerId newCustomerID
"inner stopwatch" innerStopwatch innerStopWatch
"supports IPv6 on iOS" supportsIpv6OnIos supportsIPv6OnIOS
"YouTube importer" YouTubeImporter YoutubeImporter

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

       Prose form                             Correct                                 Incorrect

"XML Http Request" XmlHttpRequest XMLHTTPRequest
"new customer ID" newCustomerId newCustomerID
"inner stopwatch" innerStopwatch innerStopWatch
"supports IPv6 on iOS" supportsIpv6OnIos supportsIPv6OnIOS
"YouTube importer" YouTubeImporter YoutubeImporter

Formatting: Naming

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

https://kotlinlang.org/docs/coding-conventions.html

https://developer.android.com/kotlin/style-guide

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

Desk

By Ali Ahmadabadiha