In Kotlin, properties are declared using the var
(mutable) or val
(immutable) keywords. A simple property declaration looks like this:
// Immutable property
val name: String = "John"
// Mutable property
var age: Int = 30
Kotlin automatically generates a getter for every property. For var
properties, it also generates a setter.
A getter is a function that is called when you access a property, and a setter is called when you assign a new value to a mutable property. You can define custom getters and setters to control how the property is accessed and modified.
class Person {
var age: Int = 0
get() = field // Return the backing field
set(value) {
if (value >= 0) {
field = value // Assign the new value to the backing field
}
}
}
In the above example, the field
keyword is a special identifier that refers to the backing field of the property.
One of the most common use cases for custom getters and setters is to perform validation. For example, you might want to ensure that a property value is within a certain range.
class Rectangle {
var width: Int = 0
set(value) {
if (value >= 0) {
field = value
} else {
throw IllegalArgumentException("Width cannot be negative")
}
}
}
You can use a custom getter to implement lazy initialization. This means that the value of the property is calculated only when it is first accessed.
class DataLoader {
val data: List<String> by lazy {
// Simulate a time-consuming data loading process
Thread.sleep(1000)
listOf("data1", "data2", "data3")
}
}
Custom getters and setters can be used to encapsulate the internal state of a class and provide a controlled API for accessing and modifying it.
class Circle {
var radius: Double = 0.0
get() = field
set(value) {
if (value >= 0) {
field = value
} else {
throw IllegalArgumentException("Radius cannot be negative")
}
}
val area: Double
get() = Math.PI * radius * radius
}
fun main() {
val circle = Circle()
circle.radius = 5.0
println("Circle area: ${circle.area}")
}
In this example, we have a Circle
class with a radius
property that has a custom setter to ensure the radius is non - negative. The area
property has a custom getter that calculates the area of the circle based on the current radius.
class DatabaseConnection {
val connection: java.sql.Connection by lazy {
// Simulate a time-consuming database connection process
Thread.sleep(2000)
java.sql.DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password")
}
}
fun main() {
val db = DatabaseConnection()
// The connection is not established yet
println("Before accessing connection")
// The connection is established now
println("Connection: ${db.connection}")
}
Here, the connection
property is initialized lazily. The database connection is established only when the connection
property is first accessed.
Avoid over - complicating your getters and setters. They should be simple and easy to understand. If you need to perform complex operations, consider moving them to separate methods.
The field
keyword is a powerful tool, but use it carefully. Make sure you understand how it works and when it is appropriate to use it.
Your getters and setters should behave in a way that is consistent with the expectations of other developers. For example, a getter should not have any side effects.
The “Kotlin property getter or setter expected” error is a common issue that can be easily resolved once you understand the core concepts of Kotlin properties, getters, and setters. By using custom getters and setters, you can perform validation, implement lazy initialization, and encapsulate the internal state of your classes. Remember to follow best practices to keep your code clean and maintainable.