Kotlin Style Guide
This guide specifically addresses Kotlin development, focusing on idiomatic practices, patterns, and Kotlin-specific considerations.
Foundational Code Standards provide the foundation, this guide extends them for Kotlin.
Formatting
Adhere to the foundational formatting stadards:
- Consistent Indentation: Use 2 spaces for indentation, 4 spaces for continuation lines.
- Line Length: Aim for 100 characters, but allow flexibility for readability.
- Whitespace: Use spaces around operators, parentheses, braces, colons, commas, and keywords for clarity.
- Brace Style: Follow K&R style (opening brace on same line, closing brace on new line).
- Blank Lines: Use 1 line to separate code sections.
-
Alignment: Align elements in documentation comments and parameter lists.
Remember, these are guidelines; adapt them for your project's needs while keeping readability in focus.
Formatted Kotlin Example Code
/**
* This class demonstrates proper code formatting following the specified style guide.
*
* **Formatting Rules:**
* - 2 spaces for indentation.
* - 4 spaces for continuation lines.
* - Max line length of 100 characters.
* - Spaces around operators, control structures, and keywords.
* - K&R brace style.
* - Consistent spacing for parameter lists and constructor arguments.
* - Doc comments with aligned descriptions.
*/
class WellFormattedCode { // (1)!
/**
* This method calculates the factorial of a given positive integer. // Doc comment for method
*
* @param n The non-negative integer for which to calculate the factorial.
* @return The factorial of n, or throws an IllegalArgumentException if n is negative.
* @throws IllegalArgumentException If the provided number (n) is negative.
*/
fun calculateFactorial(n: Int): Long { // (2)!
if (n < 0) { // (3)!
throw IllegalArgumentException("Factorial is not defined for negative numbers.")
}
var result = 1L
for (i in 2..n) { // (4)!
result *= i
}
return result
}
}
- Class name in PascalCase with a doc comment.
- Method name in camelCase with a doc comment.
- K&R brace style for blocks.
- Proper spacing around operators and control structures.
Naming Conventions
The naming conventions for Kotlin adhere to our foundational naming conventions with no exceptions.
- PascalCase for classes, interfaces, enums (definitions).
- camelCase for functions, variables, properties.
- Prefix booleans with
is
orhas
for clarity.
- Prefix booleans with
- UPPER_SNAKE_CASE for constants.
- lowercase package names, concatenated words (avoid underscores).
// Class following PascalCase for a data definition
class Goat {
// Variable following camelCase for a property
var isHappy = true
// Function following camelCase with descriptive name
fun eatGrass(amount: Int) {
println("The goat munches on $amount kg of grass")
}
// Function with UPPER_SNAKE_CASE for a constant
companion object {
const val MAX_SPEED = 40 // km/h
}
}
// Inconsistent naming (should be Goat)
class goat {
// Unclear variable name
var happy = true
// Unnecessary abbreviation in function name
fun eat(amt: Int) {
println("The goat munches on $amt kg of grass") // Should use descriptive variable 'amount'
}
// Unclear function name
fun isHealthy(g: goat): String {
val msg = if (g.happy) {
"Goat happy!"
} else {
"Goat might be sick!"
}
return msg
}
companion object {
// should be UPPER_SNAKE_CASE
const val maxSpeed = 40 // km/h
}
}
// Mixed naming convention (should be snake_case or camelCase)
fun PrintGoatInfo(goat: goat) {
println("Goat is Happy: ${goat.happy}") // Should be isHappy
println("Goat Max Speed: ${goat.maxSpeed}") // Should be MAX_SPEED
}
Documentation and Comments
Refer to the Foundational Code Standards for general commenting and documentation guidelines.
Documentation Example
/**
* Represents a goat with a name, age, and a method to determine if the goat is happy.
*
* This data class simplifies the process of creating goat objects with essential attributes
* and provides a method to assess the goat's happiness based on specific conditions.
*
* **Usage Example:**
* `
* val billy = Goat("Billy", 5)
* println(billy.isHappy(3)) // Prints true or false depending on the number of meals.
* `
*
* @property name The name of the goat.
* @property age The age of the goat in years.
* @constructor Creates a goat object with the specified name and age.
* @throws IllegalArgumentException if the age is negative.
* @return A new instance of a Goat.
*/
data class Goat(val name: String, val age: Int) {
init {
if (age < 0) throw IllegalArgumentException("Age cannot be negative")
}
/**
* Determines if the goat is happy based on the number of meals it has received.
*
* A goat is considered happy if it has been fed at least twice a day.
*
* @param meals The number of meals the goat has received today.
* @return True if the goat is happy (fed at least twice today), false otherwise.
* @throws IllegalArgumentException if the number of meals is negative.
*/
fun isHappy(meals: Int): Boolean {
if (meals < 0) throw IllegalArgumentException("Meals cannot be negative")
return meals >= 2
}
}
KDoc
Use KDoc for documenting classes, functions, and properties.
Dokka
Use Dokka, an API documentation generator, for automatic documentation in various formats.
Idioms and Best Practices
Kotlin offers a wealth of idiomatic practices and patterns that can make your code more concise, readable, and idiomatic. This section focuses on leveraging Kotlin's unique features effectively.
Collections and Ranges
Use Kotlin's standard library functions for collection manipulation.
Null Safety
Utilize Kotlin's null safety features like ?.
, ?:
, and !!
operators judiciously.
Lazy Initialization
Delegate property initialization to be calculated only when accessed for the first time.
class GoatTracker {
private val numGoatsFed: Int by lazy {
// This code to count goats is executed only on first access
countGoatsInPen()
}
private fun countGoatsInPen(): Int {
return 10 // Simulate counting goats
}
}
Data Classes
Use data classes for holding data. They provide equals()
, hashCode()
, toString()
, copy()
, and componentN()
functions automatically.
Default Values
Assign default values to constructor parameters or function parameters for optional arguments.
class Goat(val name: String = "Billy", var age: Int = 1) {
fun yell(sound: String = "Meeee!"): String {
return "$name the goat yells: $sound"
}
}
val myGoat = Goat() // Uses default name ("Billy") and age (1)
val grumpyGoat = Goat("Grumpy", 5, "Baaaaaah!") // Custom name, age, and sound
Operator Overloading
Overload operators for custom classes using functions named after the operator.
data class Age(val years: Int) {
operator fun plus(yearsToAdd: Int): Age {
return Age(years + yearsToAdd)
}
}
val goatAge = Age(2)
val olderGoatAge = goatAge + 3
Extension Functions
Add new functions to existing classes without inheritance.
Scope Functions
Use apply
, with
, run
, also
, and let
for executing a block of code within the context of an object.
String Interpolation
Embed expressions within strings using ${expression} syntax for dynamic string creation.
val goatName = "Buttercup"
val message = "The goat $goatName is ${if (myGoat.age > 3) "old" else "young"}."
Instance Checks
Use is
and !is
operators for type checks and smart casts. Use as?
for safe casts.
fun feed(animal: Any) {
if (animal is Goat) {
val goat = animal as Goat // Safe cast if it's a Goat
println("Feeding grass to ${goat.name}")
} else {
println("This animal doesn't eat grass!")
}
}
Tools and Resources
Ensuring a consistent development environment and utilizing static analysis tools are crucial steps for maintaining high code quality in Kotlin projects.
Recommended Static Analysis Tools for Kotlin
Static analysis tools help identify potential issues early in the development process. For Kotlin, several tools are particularly effective:
- detekt: A static code analysis tool for Kotlin that provides code style checks, complexity checks, and more. It is configurable and integrates well with Gradle and Maven.
- ktlint: An anti-bikeshedding Kotlin linter with built-in formatter. It enforces a consistent code style and can automatically format code per the Kotlin coding standards.
- SonarQube for Kotlin: Extends SonarQube's capabilities to Kotlin, offering code smells detection, bugs tracking, and code quality metrics.
- Android Lint: For Android projects, Android Lint includes specific checks for Kotlin code, ensuring best practices for Android development are followed.
Additional Resources
- Kotlin Coding Conventions: The official coding conventions for Kotlin by JetBrains.
- Android Kotlin Style Guide: The official style guide for Android development in Kotlin.
- Awesome Kotlin: A curated list of Kotlin resources, libraries, and tools.
IntelliJ IDEA Configuration
To align with the coding standards outlined in this guide, we provide a custom IntelliJ IDEA code style configuration. This configuration ensures that your IDE automatically adheres to the formatting rules and conventions specified herein.
- Download the .editorconfig.
- Open IntelliJ IDEA.
- Go to
File
>Settings
(on Windows and Linux) orIntelliJ IDEA
>Preferences
(on macOS). - Navigate to
Editor
>Code Style
. - Ensure the checkbox for Enable EditorConfig support is :checked: checked.
- Rename the downloaded file to
.editorconfig
. - Add the
.editorconfig
file to your project root directory.
- Download the XML config file.
- Open IntelliJ IDEA.
- Go to
File
>Settings
(on Windows and Linux) orIntelliJ IDEA
>Preferences
(on macOS). - Navigate to
Editor
>Code Style
. - Click on the gear icon (
⚙️
) next to theScheme
dropdown at the top of the window and selectImport Scheme
>IntelliJ IDEA code style XML
. - Find and select the downloaded XML file, then click
OK
. - Ensure the newly imported scheme is selected in the
Scheme
dropdown. - Click
Apply
andOK
to save the changes.
By configuring your IDE to adhere to Kotlin's style conventions and integrating static analysis tools into your development process, you can significantly enhance code quality, readability, and maintainability. These tools not only help catch potential bugs early but also ensure your codebase remains clean and consistent with Kotlin best practices.