If you are new to Kotlin, it's essential to familiarize yourself with some best practices that can benefit any project. This blog post aims to summarise these practices and provide you with valuable insights.
Recently, our team had the opportunity to collaborate with SkillerWhale for an excellent Kotlin training course. It significantly accelerated our learning process and deepened our understanding of the language. Building on this experience, we’ve compiled some of the most important practices we’ve learned to help you get started with Kotlin.
To get started, you can find the basics of Kotlin in the official Kotlin reference documentation https://kotlinlang.org/docs/reference/. It provides a comprehensive guide to understanding the language.
Additionally, it's worth noting that Kotlin has official standard basic coding conventions https://kotlinlang.org/docs/coding-conventions.html. Check on that page to allow your IDEA to automatically verify them, making your coding experience more seamless. They cover topics such as source code organization, naming rules, formatting, documentation comments, and idiomatic use of language features.
Now let's dive deeper into some specific best practices.
8 effective practices for Kotlin
1. Follow official Kotlin’s coding conventions
Kotlin has official standard basic coding conventions check https://kotlinlang.org/docs/coding-conventions.html. Plus you can configure your IDE to automatically verify them, making your coding experience more seamless.
They cover topics such as source code organisation, naming rules, formatting, documentation comments, and idiomatic use of language features.
2. Ensure Safety
With Immutability:
- Keep everything read-only unless required to change its actual value:
- Use
val
for constants and only usevar
for variables that actually need to change their value. - Prefer using immutable collections (
List
,Set
,Map
) instead of mutable ones (mutableList
,mutableSet
,mutableMap
). - Remember that arguments passed to methods are passed by value, but objects are passed by reference. Be cautious when modifying object properties inside a method, as they’d remain changed afterwards.
- Protect class variables by returning only immutable ones or copies of the data instead of the original.
- For any mutable collection within a method that requires to be returned cast it to its equivalent non-mutable version: eg return a
mutableSet
asSet
.
Regularly update your project dependencies to ensure you are using the latest versions and benefiting from bug fixes and new features.
Utilise code quality tools and static analysers, such as linting tools, code formatters, and static analysis tools like SonarQube or Detekt. These tools help identify potential issues, enforce coding standards, and improve code quality and safety.
Apply the visibility modifiers wisely: private
, protected
, internal
, and public
(default). Choose appropriate visibility to prevent exposing unnecessary implementation details. https://kotlinlang.org/docs/visibility-modifiers.html
Minimize Nullable Types:
- Use nullable types (
?
) only when necessary. Avoid overusing nullable types to ensure code clarity and reduce null pointer exceptions. - Utilize safe calls (
?.
) and the Elvis operator (?:
) with nullable variables, as they can help handle null value effectively: https://kotlinlang.org/docs/reference/null-safety.html#safe-calls https://kotlinlang.org/docs/reference/null-safety.html#elvis-operator - Be Cautious with the
!!
Operator, use it only sparingly and only when you are confident in handling the potential null pointer exception - When declaring a variable before assigning a value, consider instead to initialise the value using conditional assignments, like when, if or try:
// with "when"
val skipCode = when (initial) {
1 -> NO_ALERT_REQUIRED
2 -> MANUAL_SUPPRESSION
else -> throw UnsupportedOperationException("")
}
// or "if"
val skipCode = if (initial == 1) {
NO_ALERT_REQUIRED
} else if (...)
}
//or with "try:
val skipCode = try {
getSkipCode()
} catch (e: Exception) {
throw UnsupportedOperationException("")
}
3. Prioritize Readability and Maintainability
- Use your common sense as well as feedback from peers' reviews.
- While following conventions is essential, prioritize readability for a specific task. Strike a balance between convention and readability.
- Keep your code consistent in terms of naming conventions for variables, methods, classes, packages, etc. Resolve any name clashes if needed with more detailed naming choices.
- Ensure consistent code formatting.
- Decide on an error handling strategy (e.g., exceptions, sealed classes, Result types) and use it consistently across the project. Handle errors in a uniform manner to ensure predictability and maintainability.
- Establish consistent code organisation patterns: Group related functions and properties together, follow the principle of single responsibility and organise your code into logical modules, packages, and files. Maintain a consistent directory structure and naming conventions for files.
4. Classes and functions
For any class member (variable or function) that is not expected to interact with an object set them within the companion
object (static from Java or Python) https://kotlinlang.org/docs/object-declarations.html#companion-objects for:
- Equivalent to static variables or helper functions that are only needed when dealing with this class.
- Factories, serialisation
Consider defining strongly-linked classes within the same file:
- Data classes that are only used as return values and the class using them.
- Collections of data classes and enums used throughout a package. And representative name: such as PackageNameDomainObjects.kt
- Unless the file gets too long, in which case simply a dedicated package with each of these classes in their own files.
For each class try to only define a primary constructor and, if necessary, an init
block. https://kotlinlang.org/docs/classes.html#constructors
And consider default arguments for overloading with these you can prevent using unnecessary secondary constructors https://kotlinlang.org/docs/reference/functions.html#default-arguments
Use sealed classes for methods that either retrieve a value or exception handling https://phauer.com/2019/sealed-classes-exceptions-kotlin/
Define utilities outside any class, as top-level functions, a standard convention is to declare them under [Package/Domain/Etc]Utils.kt
file in the package. If it only belongs to a specific class, then set it within the companion
object from that given class.
Agree that makes more sense to use constructors instead of builders, The Builder Pattern solves a very common problem in object-oriented programming of how to flexibly create an immutable object without writing many constructors. But when considering a builder, we should focus on whether or not the construction is complex. If we have too simple construction patterns, the effort to create our flexible builder object may far exceed the benefit. https://www.baeldung.com/kotlin/builder-pattern
5. Methods
When calling methods try always using named-arguments, especially when you are not passing all the possible arguments: https://kotlinlang.org/docs/reference/functions.html#named-arguments
For any method’s parameter validation is better to be explicit with a require
block that would throw an IllegalParameterException
if not met https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require.html
require(number >= 0.0) { "Number must be non-negative" }
The check
function in Kotlin is used to verify certain conditions during runtime. If the condition specified in the check block is not satisfied, it throws an IllegalStateException
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require.html
check(b != 0) { "Division by zero is not allowed" }
When needing to return multiple values:
- For internal functions, you can use Pairs and Triples since the creation and handling will always be in the same class
- But public functions should instead return data classes
6. Strings
- String interpolation can be solved with String Templates to inject values into strings: https://kotlinlang.org/docs/strings.html#string-templates
"""Triple quoted Strings"""
or raw strings to preserve newlines and formatting characters, to avoid the need for escape characters: https://kotlinlang.org/docs/strings.html#raw-strings
7. Operations
==
for structural equality and ===
for referential equality https://kotlinlang.org/docs/equality.html
Use when
for:
- When you’d have used a `switch` from Java or a `case` from python.
- Code block with more than one `if` statement
- Structure with only boolean checks, including assignment (x = { when (y) { … } }).
val text = when (y) {
in 1..9 -> "less than 10"
in 10..99 -> "more than 10 but less than 100"
>99 -> "100 or more"
else -> "negative"
}
8. Comments and documentation
As with everything, follow Kotlin’s coding conventions https://kotlinlang.org/docs/coding-conventions.html#documentation-comments, like 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.
// 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 { /*...*/ }
Conclusion
In conclusion, adopting best practices in Kotlin can significantly enhance the quality, and maintainability of your codebase.
By following guidelines such as ensuring safety and immutability, keeping dependencies updated, and utilising code quality tools, you can write robust and efficient Kotlin code. Understanding language-specific features like null safety operators, visibility modifiers, and string templates further empowers you to leverage Kotlin's capabilities effectively.
By adhering to these practices, you can streamline development, improve code consistency, and create reliable and maintainable Kotlin projects for your entire team.
Author: Anna Karina Nava Soriano