Skip to main content

Kotlin Documentation

Good documentation is a cornerstone of maintainable code. In this guide, we'll explore how to write clear and effective documentation for your Kotlin projects. Whether you're working alone or in a team, proper documentation makes your code easier to understand, maintain, and extend.

Why Documentation Matters

Documentation serves several important purposes:

  • Helps other developers (and your future self) understand your code
  • Reduces onboarding time for new team members
  • Makes code maintenance easier
  • Improves the overall quality of your codebase

KDoc: Kotlin's Documentation Format

Kotlin uses a documentation format called KDoc, which is similar to JavaDoc but tailored for Kotlin's features. KDoc comments begin with /** and end with */.

Basic KDoc Structure

kotlin
/**
* This is a basic KDoc comment.
*
* @param name The name parameter
* @return A greeting message
*/
fun greet(name: String): String {
return "Hello, $name!"
}

When you call this function:

kotlin
val greeting = greet("Kotlin")
println(greeting) // Outputs: Hello, Kotlin!

Essential KDoc Tags

KDoc supports various tags to provide structured information:

@param

Documents function parameters:

kotlin
/**
* Calculates the area of a rectangle.
*
* @param width The width of the rectangle
* @param height The height of the rectangle
* @return The area of the rectangle
*/
fun calculateRectangleArea(width: Double, height: Double): Double {
return width * height
}

@return

Documents the return value:

kotlin
/**
* Checks if the provided number is even.
*
* @param number The number to check
* @return true if the number is even, false otherwise
*/
fun isEven(number: Int): Boolean {
return number % 2 == 0
}

@throws / @exception

Documents exceptions that might be thrown:

kotlin
/**
* Divides two numbers.
*
* @param dividend The number to be divided
* @param divisor The number to divide by
* @return The result of the division
* @throws ArithmeticException if divisor is zero
*/
fun divide(dividend: Int, divisor: Int): Int {
if (divisor == 0) {
throw ArithmeticException("Cannot divide by zero")
}
return dividend / divisor
}

@see

References related elements:

kotlin
/**
* Formats a date according to the specified pattern.
*
* @param date The date to format
* @param pattern The pattern to use
* @return The formatted date string
* @see java.text.SimpleDateFormat
*/
fun formatDate(date: Date, pattern: String): String {
val formatter = SimpleDateFormat(pattern)
return formatter.format(date)
}

Documenting Different Kotlin Elements

Classes

kotlin
/**
* Represents a user in the system.
*
* @property id The unique identifier for the user
* @property name The name of the user
* @property email The email address of the user
* @constructor Creates a new User with the specified details
*/
class User(val id: Int, val name: String, val email: String) {
/**
* Checks if the user has a valid email format.
*
* @return true if the email is valid, false otherwise
*/
fun hasValidEmail(): Boolean {
return email.contains("@") && email.contains(".")
}
}

Properties

kotlin
class Temperature {
/**
* The temperature in Celsius.
* Valid range is -273.15°C (absolute zero) and above.
*/
var celsius: Double = 0.0
set(value) {
if (value < -273.15) {
throw IllegalArgumentException("Temperature cannot be below absolute zero")
}
field = value
}

/**
* The temperature in Fahrenheit.
* Calculated based on the Celsius value.
*/
val fahrenheit: Double
get() = celsius * 9 / 5 + 32
}

Extension Functions

kotlin
/**
* Checks if the string is a valid email address format.
*
* @return true if the string is a valid email format, false otherwise
*/
fun String.isValidEmail(): Boolean {
val emailRegex = Regex("[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
return matches(emailRegex)
}

Documentation Best Practices

1. Be Clear and Concise

Write documentation that is easy to understand while avoiding unnecessary verbosity:

kotlin
// Good
/**
* Calculates the sum of two integers.
*
* @param a First integer
* @param b Second integer
* @return The sum of a and b
*/
fun sum(a: Int, b: Int): Int

// Bad - too verbose
/**
* This function takes two integer parameters and returns an integer that is the result
* of adding the first integer parameter to the second integer parameter. The function
* is named sum because it performs addition.
*
* @param a This is the first integer parameter that will be used in the addition operation
* @param b This is the second integer parameter that will be used in the addition operation
* @return Returns an integer representing the sum of the two input parameters
*/
fun sum(a: Int, b: Int): Int

2. Document Non-Obvious Behavior

Focus on explaining behavior that isn't immediately obvious:

kotlin
/**
* Parses a date string using the specified format.
*
* Note: This function is not thread-safe due to the underlying
* SimpleDateFormat implementation.
*
* @param dateString The date string to parse
* @param format The format pattern
* @return The parsed Date object
* @throws ParseException if the string cannot be parsed
*/
fun parseDate(dateString: String, format: String): Date {
val formatter = SimpleDateFormat(format)
return formatter.parse(dateString)
}

3. Use Code Examples

Include examples for complex functions:

kotlin
/**
* Filters a list to include only elements that match the predicate.
*
* Example:
* ```
* val numbers = listOf(1, 2, 3, 4, 5)
* val evenNumbers = numbers.filterCustom { it % 2 == 0 }
* // evenNumbers = [2, 4]
* ```
*
* @param predicate The function that determines if an element should be included
* @return A new list containing only the elements that match the predicate
*/
fun <T> List<T>.filterCustom(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) {
result.add(item)
}
}
return result
}

4. Document APIs Completely

For public APIs, document all public elements:

kotlin
/**
* Utility class for string operations.
*/
object StringUtils {
/**
* Reverses a string.
*
* @param input The string to reverse
* @return The reversed string
*/
fun reverse(input: String): String {
return input.reversed()
}

/**
* Counts the occurrences of a character in a string.
*
* @param input The string to search in
* @param char The character to count
* @return The number of occurrences
*/
fun countChar(input: String, char: Char): Int {
return input.count { it == char }
}
}

Real-World Example: Documented API Client

Here's a more comprehensive example showing a well-documented HTTP client class:

kotlin
/**
* A client for making HTTP requests to the Weather API.
*
* This client handles authentication, request formatting, and response parsing
* for interactions with the Weather API.
*
* Example usage:
* ```
* val client = WeatherApiClient("your-api-key")
* val forecast = client.getForecast("London", 5)
* println("5-day forecast for London: ${forecast.summary}")
* ```
*
* @property apiKey The API key used for authentication
* @property baseUrl The base URL for the Weather API
*/
class WeatherApiClient(
private val apiKey: String,
private val baseUrl: String = "https://api.weatherservice.com/v1"
) {
/**
* Retrieves the current weather for the specified location.
*
* @param location The city name or coordinates
* @return The current weather data
* @throws ApiException if the request fails
* @throws AuthenticationException if the API key is invalid
*/
fun getCurrentWeather(location: String): WeatherData {
// Implementation details...
return WeatherData(
temperature = 22.5,
condition = "Sunny",
humidity = 45,
windSpeed = 10.0
)
}

/**
* Retrieves a weather forecast for the specified number of days.
*
* Note: The maximum forecast period is 10 days. Requesting more than
* 10 days will automatically be limited to 10.
*
* @param location The city name or coordinates
* @param days The number of days to forecast (1-10)
* @return The forecast data
* @throws ApiException if the request fails
* @throws IllegalArgumentException if days is less than 1
*/
fun getForecast(location: String, days: Int): ForecastData {
require(days > 0) { "Days must be positive" }

val actualDays = minOf(days, 10)
// Implementation details...

return ForecastData(
location = location,
days = actualDays,
summary = "Mostly sunny with occasional rain",
dailyForecasts = List(actualDays) { day ->
DailyForecast(
day = day + 1,
highTemp = 25.0 - day,
lowTemp = 15.0 - day,
condition = if (day % 3 == 0) "Rainy" else "Sunny"
)
}
)
}
}

/**
* Represents current weather conditions.
*
* @property temperature The current temperature in Celsius
* @property condition The weather condition (e.g., "Sunny", "Cloudy")
* @property humidity The humidity percentage (0-100)
* @property windSpeed The wind speed in km/h
*/
data class WeatherData(
val temperature: Double,
val condition: String,
val humidity: Int,
val windSpeed: Double
)

/**
* Represents a weather forecast for multiple days.
*
* @property location The location for this forecast
* @property days The number of days in the forecast
* @property summary A summary of the overall forecast
* @property dailyForecasts The forecast for each day
*/
data class ForecastData(
val location: String,
val days: Int,
val summary: String,
val dailyForecasts: List<DailyForecast>
)

/**
* Represents the forecast for a single day.
*
* @property day The day number (1 = tomorrow)
* @property highTemp The highest expected temperature in Celsius
* @property lowTemp The lowest expected temperature in Celsius
* @property condition The expected weather condition
*/
data class DailyForecast(
val day: Int,
val highTemp: Double,
val lowTemp: Double,
val condition: String
)

Tools for Generating Documentation

Kotlin documentation can be generated using tools like:

  1. Dokka: The official documentation generation tool for Kotlin

    bash
    # Add to build.gradle.kts
    plugins {
    id("org.jetbrains.dokka") version "1.8.10"
    }
  2. IntelliJ IDEA: Provides built-in support for KDoc with syntax highlighting and code completion

Summary

Good documentation is an essential part of writing maintainable Kotlin code:

  • Use KDoc format starting with /** and ending with */
  • Document all public API elements including classes, functions, and properties
  • Use tags like @param, @return, and @throws for structured information
  • Include examples for complex functionality
  • Be clear and concise while explaining non-obvious behavior

By following these best practices, you'll create code that's easier to understand, maintain, and collaborate on.

Additional Resources

Exercises

  1. Add proper KDoc documentation to an existing class in one of your projects
  2. Generate documentation for a Kotlin project using Dokka
  3. Review a colleague's code and suggest documentation improvements
  4. Document a complex algorithm explaining each step in the KDoc comments


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)