Kotlin Expression Bodies
Introduction
In Kotlin, functions and properties can be defined in a concise way using expression bodies. An expression body is a compact syntax that allows you to define a function or property that returns the value of a single expression without using curly braces and an explicit return
statement. This feature contributes to Kotlin's goal of being more expressive and concise than many other programming languages.
Expression bodies are particularly useful when you want to create simple functions that compute and return a value without complex logic. This lesson will help you understand how to use expression bodies effectively in your Kotlin programs.
Basic Syntax
Let's first look at a regular function with a traditional body:
fun sum(a: Int, b: Int): Int {
return a + b
}
The same function can be rewritten using an expression body:
fun sum(a: Int, b: Int): Int = a + b
Notice the key differences:
- The curly braces
{}
are gone - The
return
keyword is removed - The
=
symbol replaces the opening brace - The function body consists of a single expression
Type Inference with Expression Bodies
When using expression bodies, Kotlin can often infer the return type, allowing you to omit it:
fun sum(a: Int, b: Int) = a + b
The compiler knows that a + b
results in an Int
, so specifying the return type is optional. However, for clarity and documentation purposes, it's often a good practice to include return types in function signatures.
Examples of Expression Bodies
Simple Calculations
fun multiply(a: Int, b: Int) = a * b
fun divide(a: Double, b: Double) = a / b
fun main() {
println("4 * 5 = ${multiply(4, 5)}")
println("10.0 / 2.5 = ${divide(10.0, 2.5)}")
}
Output:
4 * 5 = 20
10.0 / 2.5 = 4.0
String Operations
fun greet(name: String) = "Hello, $name!"
fun getInitials(fullName: String) = fullName.split(" ")
.map { it.first().toUpperCase() }
.joinToString("")
fun main() {
println(greet("Kotlin Developer"))
println("Initials of 'John Doe': ${getInitials("John Doe")}")
}
Output:
Hello, Kotlin Developer!
Initials of 'John Doe': JD
Conditional Logic
Expression bodies can also contain conditional expressions:
fun max(a: Int, b: Int) = if (a > b) a else b
fun temperatureDescription(celsius: Double) = when {
celsius < 0 -> "Freezing"
celsius < 10 -> "Cold"
celsius < 20 -> "Cool"
celsius < 30 -> "Warm"
else -> "Hot"
}
fun main() {
println("Max of 7 and 12: ${max(7, 12)}")
println("23.5°C is ${temperatureDescription(23.5)}")
}
Output:
Max of 7 and 12: 12
23.5°C is Warm
Expression Bodies for Properties
Expression bodies aren't limited to functions. They can also be used with properties:
class Rectangle(val width: Int, val height: Int) {
val area = width * height
val perimeter = 2 * (width + height)
val isSquare = width == height
}
fun main() {
val rect = Rectangle(5, 10)
println("Area: ${rect.area}")
println("Perimeter: ${rect.perimeter}")
println("Is square: ${rect.isSquare}")
}
Output:
Area: 50
Perimeter: 30
Is square: false
When to Use Expression Bodies
Expression bodies are ideal when:
- A function returns a single expression
- The function logic is simple and can be expressed in one line
- You want to make your code more concise and readable
However, not all functions should use expression bodies. When your function contains multiple statements, requires complex logic, or has side effects beyond returning a value, a traditional function body with curly braces is more appropriate.
Real-World Applications
UI Event Handlers
// In Android development
button.setOnClickListener { view -> navigateToNextScreen() }
// The navigateToNextScreen function could be defined as:
fun navigateToNextScreen() = findNavController().navigate(R.id.action_current_to_next)
Data Transformation in a Chain
fun getUserFullNames(users: List<User>) = users
.filter { it.isActive }
.map { "${it.firstName} ${it.lastName}" }
// In a real application
fun main() {
val users = listOf(
User("John", "Doe", true),
User("Jane", "Smith", false),
User("Bob", "Johnson", true)
)
val activeUserNames = getUserFullNames(users)
println("Active users: $activeUserNames")
}
data class User(val firstName: String, val lastName: String, val isActive: Boolean)
Output:
Active users: [John Doe, Bob Johnson]
Configuration Functions
fun defaultSettings() = Settings(
darkMode = true,
fontSize = 14,
language = "English"
)
data class Settings(val darkMode: Boolean, val fontSize: Int, val language: String)
fun main() {
val settings = defaultSettings()
println("Default settings: $settings")
}
Output:
Default settings: Settings(darkMode=true, fontSize=14, language=English)
Common Pitfalls and Best Practices
Be Careful with Side Effects
Although technically possible, it's not considered good practice to include functions with side effects in expression bodies:
// Not recommended
fun printAndReturn(message: String) = println(message).also { "Message printed" }
// Better approach
fun printAndReturn(message: String): String {
println(message)
return "Message printed"
}
Keep It Simple
Expression bodies should be simple and readable. If the expression is too complex, consider using a regular function body:
// Too complex for an expression body
fun calculateComplexFormula(x: Double, y: Double) =
Math.pow(x, 2.0) * Math.sin(y) + Math.sqrt(Math.abs(x * y)) / (1 + Math.exp(x - y))
// Better as a regular function
fun calculateComplexFormula(x: Double, y: Double): Double {
val term1 = Math.pow(x, 2.0) * Math.sin(y)
val term2 = Math.sqrt(Math.abs(x * y))
val term3 = 1 + Math.exp(x - y)
return term1 + term2 / term3
}
Consider Readability
Always prioritize readability over conciseness:
// Technically correct but harder to understand
fun isEligible(user: User) = user.age >= 18 && user.hasValidId && (!user.isBanned || user.hasSpecialPermission)
// More readable
fun isEligible(user: User): Boolean {
if (user.isBanned && !user.hasSpecialPermission) {
return false
}
return user.age >= 18 && user.hasValidId
}
Summary
Kotlin expression bodies are a powerful feature that allows you to write more concise and expressive code. By replacing traditional function bodies with a single expression, you can make your code more readable for simple operations.
Key points to remember:
- Expression bodies use the
=
symbol instead of curly braces - They're ideal for simple functions that return a single expression
- The return type can often be inferred by the compiler
- They can be used for both functions and properties
- They work well with conditional expressions like
if
andwhen
- Always prioritize readability over extreme conciseness
Exercises
-
Convert the following function to use an expression body:
kotlinfun isEven(number: Int): Boolean {
return number % 2 == 0
} -
Write a function with an expression body that returns the absolute value of a number.
-
Create a function that converts a temperature from Fahrenheit to Celsius using an expression body.
-
Implement a
Person
class with properties forfirstName
andlastName
, and add a propertyfullName
with an expression body. -
Write a function that checks if a string is a palindrome (reads the same backward as forward) using an expression body.
Additional Resources
- Kotlin Official Documentation: Functions
- Kotlin Style Guide
- Book: "Kotlin in Action" by Dmitry Jemerov and Svetlana Isakova
- Online Course: "Kotlin for Java Developers" on Coursera
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)