Kotlin - Properties and Fields

Properties and Fields in Kotlin

Kotlin is a statically typed programming language that aims to make code more concise and readable while maintaining type safety. One of its most notable features is the way it handles properties and fields. These constructs are integral to object-oriented programming, as they manage the internal state of classes and objects. In this guide, we will dive deep into Kotlin properties and fields, covering their declaration, initialization, customization with getters and setters, backing fields, late initialization, delegation, and more.

What Are Properties in Kotlin?

In Kotlin, a property is a combination of a field and its associated getter and setter methods. Every property has a backing field (optional) and accessor functions (getter and setter). A property in Kotlin is similar to a variable, but with more capabilities such as custom accessors.


class Person {
    var name: String = "John Doe"
    var age: Int = 30
}
    

In this example, name and age are properties of the class Person. Each of these properties has an automatically generated getter and setter.

Default Getters and Setters

Kotlin provides default getter and setter methods for properties. The default getter returns the value of the property, and the default setter sets the value.


class Car {
    var model: String = "Sedan"
    var speed: Int = 100
}
    

When you access car.model, it internally calls the getter, and when you assign a value to car.speed, it calls the setter. You can override these methods for customization.

Custom Getters and Setters

You can define your own getter and setter to add custom logic to property access.

Custom Getter


class Temperature {
    var celsius: Double = 0.0
    val fahrenheit: Double
        get() = (celsius * 9/5) + 32
}
    

Here, fahrenheit is a read-only property that calculates its value based on celsius. It does not have a backing field.

Custom Setter


class User {
    var password: String = ""
        set(value) {
            if (value.length >= 8) {
                field = value
            } else {
                println("Password too short")
            }
        }
}
    

In this case, the set accessor includes validation logic. The keyword field refers to the backing field.

Backing Fields

Kotlin generates a hidden backing field for a property when you use the field identifier inside its getter or setter. The backing field stores the actual value of the property.


class Employee {
    var salary: Int = 0
        set(value) {
            if (value > 0) field = value
        }
}
    

Without using field, you would get a recursive call error as the setter would call itself.

Late Initialization

Sometimes, you want to declare a property without initializing it in the constructor. Kotlin provides lateinit for this purpose, but only for var (mutable) non-nullable properties.


class Database {
    lateinit var connection: String

    fun connect() {
        connection = "Connected to DB"
    }

    fun checkConnection() {
        if (::connection.isInitialized) {
            println(connection)
        } else {
            println("Not initialized")
        }
    }
}
    

The ::connection.isInitialized syntax checks if the property has been initialized.

Immutable Properties (val)

When a property is declared using val, it cannot be reassigned after initialization. It is similar to a final variable in Java.


class Book {
    val title: String = "Kotlin in Action"
}
    

This is useful when you want to make sure a property does not change after it is set.

Computed Properties

Computed properties are properties that do not store a value but compute it on access.


class Circle(val radius: Double) {
    val area: Double
        get() = Math.PI * radius * radius
}
    

Here, area is a computed property without a backing field.

Delegated Properties

Kotlin allows delegation of property behavior to another object using the by keyword. Kotlin provides several standard delegates such as lazy, observable, and vetoable.

Lazy Delegation


val lazyValue: String by lazy {
    println("Computed!")
    "Hello"
}
    

The value is computed only once when accessed the first time.

Observable Delegation


import kotlin.properties.Delegates

var name: String by Delegates.observable("Initial") { _, old, new ->
    println("Changed from $old to $new")
}
    

Vetoable Delegation


import kotlin.properties.Delegates

var age: Int by Delegates.vetoable(0) { _, _, new ->
    new >= 18
}
    

The property will only change if the condition evaluates to true.

Property Visibility

Kotlin allows you to set different visibility modifiers on properties:

  • public – visible everywhere
  • private – visible only within the class
  • protected – visible in class and subclasses
  • internal – visible in the same module

class SecureData {
    private var key: String = "secret"
}
    

Property Overriding

Kotlin supports overriding properties in subclasses.


open class Animal {
    open val sound: String = "Some sound"
}

class Dog : Animal() {
    override val sound: String = "Bark"
}
    

Interface Properties

Interfaces can declare properties, but they do not have backing fields. They must be implemented by classes.


interface Shape {
    val area: Double
}

class Square(val side: Double) : Shape {
    override val area: Double
        get() = side * side
}
    

Top-Level Properties

Kotlin allows you to declare properties outside of any class, object, or function — these are called top-level properties.


val companyName = "OpenAI"

fun printCompany() {
    println(companyName)
}
    

Compile-Time Constants

If you want a compile-time constant, use the const modifier. It can only be used with val and primitive types or String.


const val PI = 3.14159
    

Backing Properties

In some cases, you want to control property access more granularly. You can use a private backing property.


class Person {
    private var _email: String = ""
    var email: String
        get() = _email
        set(value) {
            if (value.contains("@")) {
                _email = value
            } else {
                println("Invalid email")
            }
        }
}
    

Summary

Properties and fields in Kotlin provide a flexible and expressive way to define class and object data. By leveraging default and custom accessors, backing fields, late initialization, delegation, and visibility modifiers, you can write highly maintainable and secure Kotlin code.

  • Use val for immutable properties and var for mutable ones.
  • Customize access with get() and set() as needed.
  • Use lateinit for properties that will be initialized later.
  • Use by for delegated properties to add flexibility.

Beginner 5 Hours

Properties and Fields in Kotlin

Kotlin is a statically typed programming language that aims to make code more concise and readable while maintaining type safety. One of its most notable features is the way it handles properties and fields. These constructs are integral to object-oriented programming, as they manage the internal state of classes and objects. In this guide, we will dive deep into Kotlin properties and fields, covering their declaration, initialization, customization with getters and setters, backing fields, late initialization, delegation, and more.

What Are Properties in Kotlin?

In Kotlin, a property is a combination of a field and its associated getter and setter methods. Every property has a backing field (optional) and accessor functions (getter and setter). A property in Kotlin is similar to a variable, but with more capabilities such as custom accessors.

class Person { var name: String = "John Doe" var age: Int = 30 }

In this example, name and age are properties of the class Person. Each of these properties has an automatically generated getter and setter.

Default Getters and Setters

Kotlin provides default getter and setter methods for properties. The default getter returns the value of the property, and the default setter sets the value.

class Car { var model: String = "Sedan" var speed: Int = 100 }

When you access car.model, it internally calls the getter, and when you assign a value to car.speed, it calls the setter. You can override these methods for customization.

Custom Getters and Setters

You can define your own getter and setter to add custom logic to property access.

Custom Getter

class Temperature { var celsius: Double = 0.0 val fahrenheit: Double get() = (celsius * 9/5) + 32 }

Here, fahrenheit is a read-only property that calculates its value based on celsius. It does not have a backing field.

Custom Setter

class User { var password: String = "" set(value) { if (value.length >= 8) { field = value } else { println("Password too short") } } }

In this case, the set accessor includes validation logic. The keyword field refers to the backing field.

Backing Fields

Kotlin generates a hidden backing field for a property when you use the field identifier inside its getter or setter. The backing field stores the actual value of the property.

class Employee { var salary: Int = 0 set(value) { if (value > 0) field = value } }

Without using field, you would get a recursive call error as the setter would call itself.

Late Initialization

Sometimes, you want to declare a property without initializing it in the constructor. Kotlin provides lateinit for this purpose, but only for var (mutable) non-nullable properties.

class Database { lateinit var connection: String fun connect() { connection = "Connected to DB" } fun checkConnection() { if (::connection.isInitialized) { println(connection) } else { println("Not initialized") } } }

The ::connection.isInitialized syntax checks if the property has been initialized.

Immutable Properties (val)

When a property is declared using val, it cannot be reassigned after initialization. It is similar to a final variable in Java.

class Book { val title: String = "Kotlin in Action" }

This is useful when you want to make sure a property does not change after it is set.

Computed Properties

Computed properties are properties that do not store a value but compute it on access.

class Circle(val radius: Double) { val area: Double get() = Math.PI * radius * radius }

Here, area is a computed property without a backing field.

Delegated Properties

Kotlin allows delegation of property behavior to another object using the by keyword. Kotlin provides several standard delegates such as lazy, observable, and vetoable.

Lazy Delegation

val lazyValue: String by lazy { println("Computed!") "Hello" }

The value is computed only once when accessed the first time.

Observable Delegation

import kotlin.properties.Delegates var name: String by Delegates.observable("Initial") { _, old, new -> println("Changed from $old to $new") }

Vetoable Delegation

import kotlin.properties.Delegates var age: Int by Delegates.vetoable(0) { _, _, new -> new >= 18 }

The property will only change if the condition evaluates to true.

Property Visibility

Kotlin allows you to set different visibility modifiers on properties:

  • public – visible everywhere
  • private – visible only within the class
  • protected – visible in class and subclasses
  • internal – visible in the same module
class SecureData { private var key: String = "secret" }

Property Overriding

Kotlin supports overriding properties in subclasses.

open class Animal { open val sound: String = "Some sound" } class Dog : Animal() { override val sound: String = "Bark" }

Interface Properties

Interfaces can declare properties, but they do not have backing fields. They must be implemented by classes.

interface Shape { val area: Double } class Square(val side: Double) : Shape { override val area: Double get() = side * side }

Top-Level Properties

Kotlin allows you to declare properties outside of any class, object, or function — these are called top-level properties.

val companyName = "OpenAI" fun printCompany() { println(companyName) }

Compile-Time Constants

If you want a compile-time constant, use the const modifier. It can only be used with val and primitive types or String.

const val PI = 3.14159

Backing Properties

In some cases, you want to control property access more granularly. You can use a private backing property.

class Person { private var _email: String = "" var email: String get() = _email set(value) { if (value.contains("@")) { _email = value } else { println("Invalid email") } } }

Summary

Properties and fields in Kotlin provide a flexible and expressive way to define class and object data. By leveraging default and custom accessors, backing fields, late initialization, delegation, and visibility modifiers, you can write highly maintainable and secure Kotlin code.

  • Use val for immutable properties and var for mutable ones.
  • Customize access with get() and set() as needed.
  • Use lateinit for properties that will be initialized later.
  • Use by for delegated properties to add flexibility.

Related Tutorials

Frequently Asked Questions for Kotlin

Companion objects hold static members, like Java’s static methods, in Kotlin classes.

A concise way to define anonymous functions using { parameters -> body } syntax.

Kotlin prevents null pointer exceptions using nullable (?) and non-null (!!) type syntax.

Inline functions reduce overhead by inserting function code directly at call site.

JetBrains, the makers of IntelliJ IDEA, developed Kotlin and released it in 2011.

Allows non-null variables to be initialized after declaration (used with var only).

val is immutable (read-only), var is mutable (can change value).

Compiler automatically determines variable types, reducing boilerplate code.

A data class automatically provides equals(), hashCode(), toString(), and copy() methods.

A function that takes functions as parameters or returns them.

Kotlin is a modern, statically typed language that runs on the Java Virtual Machine (JVM).

They add new methods to existing classes without modifying their source code.

It allows unpacking data class properties into separate variables.

== checks value equality; === checks reference (memory) equality.


apply is a scope function to configure an object and return it.

A class that restricts subclassing, useful for representing restricted class hierarchies.

Coroutines enable asynchronous programming by suspending and resuming tasks efficiently.

Functions can define default values for parameters, avoiding overloads.

Kotlin offers concise syntax, null safety, and modern features not found in Java.

Kotlin automatically casts variables to appropriate types after type checks.

Use the object keyword to create a singleton.

Calls a method only if the object is non-null.

Yes, Kotlin supports backend development using frameworks like Ktor and Spring Boot.

Data structures like List, Set, and Map, supporting functional operations.

line

Copyrights © 2024 letsupdateskills All rights reserved