Skip to content

Instantly share code, notes, and snippets.

@BomBardyGamer
Created April 16, 2021 18:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BomBardyGamer/de0882d8789d8e0ebae04fdb3621d0e3 to your computer and use it in GitHub Desktop.
Save BomBardyGamer/de0882d8789d8e0ebae04fdb3621d0e3 to your computer and use it in GitHub Desktop.
Bardy's Kotlin style guide

Bardy's Kotlin style guide

Table of contents

  • Introduction
  • Styling
    • Source files
    • Formatting
    • Naming

Introduction

This is a complete definition of my style guide for the Kotlin programming language.

This guide contains information about both personal uses and recommendations as well as styles and conventions not permitted.

Definitions

Type - A class, interface, or object. Modifiers are not applicable here (e.g. enum class counts as a class).

Primary type - Referring to a type in a file with multiple type, this is generally a type that is inherited by other types in the file, such as a sealed class.

Strictly required - Something that I will throw out and literally throw up at if I see.

Styling

The main part of this document is, of course, the actual style. This contains information on what is allowed and what isn't.

Source files

A source file is a file that contains some form of source code, and these files generally end with the .kt file extension for Kotlin.

Basic file information

  • Naming:
    • If the file contains a single type (class, interface, etc.), the name of the file should be the exact name of the class
    • If the file contains multiple types, the name of the file is generally the name of the type at the top (which is often what I will call here the "primary" one)
    • If the file contains no types, it should be named in camelCase.
  • Encoding should be in UTF-8
  • Special characters:
    • Whitespace should only be the ASCII horizontal space character (0x20), not any other character. This implies that tab characters must not be used for
    • indentation, and that all other whitespace characters must be properly escaped.
    • Escape sequences should be preferred over their octal or Unicode representation.
    • Non-ASCII characters should either use their character representation, or a Unicode escape character. The former should be preferred for readability.

Structure

  • Files should be ordered in the following order:
    • License or copyright information, if present.
    • Package statement
    • Import statements
    • If a type is present in the file, this should be ordered first, unless it is an internal part of the project and is used by other members within that file, such as a type that is constructed by top-level functions.
    • Top-level functions and properties
  • Files can optionally end with a blank line, but this is not recommended, and I personally do not do this.

Package statement

The package statement is never wrapped, nor does it adhere to any column limits. This should always be a single line.

Import statements

  • Wildcard imports are openly permitted and, in fact, highly encouraged, as they help to reduce the amount of import statements present in a file. My personal setting for the number of types before producing a wildcard import is three.
  • Import statements are also never wrapped, and do not adhere to any column limits. These should always be single lines.

Type declaration

  • As this is Kotlin, multiple types residing in a single file is permitted, though it is highly recommended to only put types in the same file as one another:
    • If it is required, such as for sealed classes. In this case, the sealed class should be put at the top of the file, and the implementations should follow.
    • If they share something that should only be available to them, such as a private top-level function or property (as private in the file context means it is accessible only to the file's members)
  • The type's declaration should always begin with the type's keyword, followed by its name on the same line, then its primary constructor parameters, which can be separated by a line break in Kotlin style (see below), followed by its parents, which may also be separated by a line break.
    • An example of this:
      data class Person(val name: String, val age: Int) : Comparable<Person>
    • As seen above, the braces may also be omitted, and it is highly recommended to do so as well, as they are completely unnecessary.
  • There should always be a line break following the initial declaration before any members are declared (this is optional, but highly recommended, and what I personally use). An example of this would be:
    class MyClass {
    
        fun foo() = Unit
    }
Ordering of type contents

This is mostly personal preference, but I personally use and recommend the following:

  • Property declarations, e.g. vals and vars
  • Abstract functions (for abstract classes, sealed classes and interfaces)
  • Public functions with a default/final functions (no overridden functions)
  • Overridden functions
  • Non-public functions
  • Extension or backing properties (properties with no value and a custom getter, such as those used to replace getX methods from Java)
  • Nested types
  • The companion object

An example of this can be seen here:

inline class IntWrapper(val value: Int) : Comparable<IntWrapper> {

    val byteValue = value.toByte()

    operator fun plus(other: Int) = value + other

    override fun compareTo(other: IntWrapper) = value.compareTo(other.value)

    private fun hash() = hashCode()

    val string: String
        get() = value.toString()

    class IntTransformer {

        fun transform(value: Int)
    }

    companion object {

        const val ONE = 1
    }
}

(I know that's a terrible example, but hey, at least it has everything in the right order)

Formatting

Covers most of the actual formatting information.

Some common things:

  • Expression functions are permitted, and even encouraged, for functions with a single statement. I personally take this luxury very far, and very much overuse these, which you are free to also do if you like.
  • Overridden functions that you do not want to implement functionality for that return Unit should always be implemented as fun doNothing() = Unit, rather than fun doNothing() {}.
  • There should be one statement per line. The semicolon (;) separator should not be used at all, except for in cases where you want to set a var's setter to a different visibility, or annotate it, e.g. val myValue = 0; private set.
  • The recommended column limit is 120 characters (the default in the IntelliJ IDE). Any code that exceeds this should be split per call, for example:
    val string = StringBuilder("Hello")
        .append(' ')
        .append("World")
        .append('!')
        .toString()

Indentation

At all times, the indentation should be exactly four spaces. This is not optional. Two spaces, or the significantly more horrifying eight spaces, are not permitted. Anywhere.

Brackets

Brackets should always be in the Kernighan and Ritchie style, specifically the one true brace style (I'm not kidding, that's what it's called) variant.

Information about this style can be found on the Wikipedia page

Control flow statements

If statements

There are three different ways you can write a single statement if statement (one that only does one thing), those being:

  • On a single line:

    if (condition) return
  • On the next line without braces:

    if (condition)
        return
  • On the next line with braces:

    if (condition) {
        return
    }
  • The first one is the one that I recommend and most commonly use, unless the statement (in the above case, return) is too long to fit on a single line, in which the third one should be used. I believe that using a single line here helps to keep your code concise.

  • The second one is not permitted under any circumstances.

  • The third one is an alternative to the first, and can be used if preferred.

In addition, the third one should be used anywhere that there is more than a single condition (of course), and there is one more exception to do with else statements. That being that if statements should always be braced if the entire if (condition) x else y cannot fit on a single line cleanly like that.

if (condition) x
else y

The above is not recommended, and preferrably should not be used.

Others

All other control flow statements are generally more than a single line anyway, so will mostly be braced, but single line for and while statements are permitted.

when statements are always braced.

Horizontal whitespace

Other than where it is required by the language, a single ASCII space appears in the following places:

  • Separating any keyword, such as if, for or catch, from an open parenthesis ((). This is strictly required.
  • Separating any keyword, such as else or catch, from a closing curly bracket (}). This is also strictly required.
  • Before any open curly bracket ({).
  • On both sides of any binary operator, such as a + or =.
  • After ,:;
  • On both sides of the double slash (//) that begins an end-of-line comment. Multiple spaces are permitted here, but not required.

Horizontal alignment

This must not be used. Anywhere. Ever.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment