KOTLIN: Classes in Kotlin

Classes in Kotlin are the primary mechanism for defining custom types. They support properties with custom getters and setters, primary constructors with concise syntax, and initialization blocks. This post explores how Kotlin classes work and how their design eliminates common Java boilerplate.

In Kotlin, a class is the only way to create a custom type. If you need to represent a Person, a BankAccount, a NetworkRequest, or any other domain concept — you define a class.

Kotlin classes are designed to be concise. The same structures that require dozens of lines in Java can be expressed in a handful of lines in Kotlin, without sacrificing readability or capability. This conciseness is not just cosmetic — it means less code to write, less code to read, and fewer places for bugs to hide.


The Structure of a Kotlin Class

A Kotlin class can contain:

  • Properties — data the class holds, with optional custom access logic
  • Primary Constructor — the main way to create instances and initialize properties
  • Functions — behavior the class exposes
  • Initialization Blocks — code that runs during construction
  • Secondary Constructors — alternative construction paths

md
+------------------------------------+
|           Kotlin Class             |
|                                    |
|   Properties                       |
|   +----------------------------+   |
|   | val name: String           |   |
|   | var weight: Double         |   |
|   +----------------------------+   |
|                                    |
|   Primary Constructor              |
|   +----------------------------+   |
|   | class Foo(val x: Int)      |   |
|   +----------------------------+   |
|                                    |
|   Functions / Init Blocks          |
|   +----------------------------+   |
|   | fun doThing() { ... }      |   |
|   | init { ... }               |   |
|   +----------------------------+   |
+------------------------------------+


Properties

Properties are the data members of a Kotlin class. They are more powerful than simple Java fields — each property can have custom logic for reading and writing its value via getter and setter functions.

Basic Properties

kotlin
class Person {
    val name: String = "Jim"    // Immutable property
    var weightLbs: Double = 0.0  // Mutable property
}

val p = Person()
val name = p.name       // Accessing a property reads its value
p.weightLbs = 220.0     // Assigning to a var property writes its value

Properties are accessed with dot notation — no getName() or setWeight() methods needed. Kotlin generates appropriate accessor methods for Java interoperability under the hood, but your Kotlin code never sees them.

Custom Getters and Setters

Sometimes a property's stored value should differ from the value you read or write. Kotlin lets you define custom getter and setter logic directly on the property definition.

kotlin
class Person {
    val name: String = "Jim"
    var weightLbs: Double = 0.0

    var weightKilos: Double
        get() = weightLbs / 2.2         // Custom getter: converts lbs to kg
        set(value) {
            weightLbs = value * 2.2     // Custom setter: converts kg to lbs
        }
}

val p = Person()
p.weightLbs = 220.0

// Accessing weightKilos runs the getter, returns ~100.0
val kilos = p.weightKilos

// Assigning to weightKilos runs the setter, updates weightLbs
p.weightKilos = 50.0

// weightLbs is now 110.0
val lbs = p.weightLbs

Custom getters and setters allow you to expose a computed or converted view of data without storing redundant values. In this example, only weightLbs is stored as a backing field — weightKilos is derived on demand.

This is a powerful encapsulation tool: from the outside, both look like simple properties. The conversion logic is hidden inside the class.


Primary Constructor

Every Kotlin class can have a primary constructor — the main constructor that defines the parameters required to create an instance and can initialize properties.

The primary constructor is declared as part of the class header, after the class name. The constructor keyword is optional when there are no annotations or visibility modifiers:

kotlin
// Verbose form with explicit constructor keyword
class Person constructor(name: String, weightLbs: Double) {
    val name: String = name
    var weightLbs: Double = weightLbs
    var weightKilos: Double
        get() = weightLbs / 2.2
        set(value) {
            weightLbs = value * 2.2
        }
}

This is already more concise than Java, but Kotlin goes further. You can move the property declaration directly into the constructor parameter list using val or var:

kotlin
// Concise form — properties declared in the constructor header
class Person(val name: String, var weightLbs: Double) {
    var weightKilos: Double
        get() = weightLbs / 2.2
        set(value) {
            weightLbs = value * 2.2
        }
}

When you write val name: String in the constructor, Kotlin automatically:

  • Declares a property named name of type String
  • Assigns the constructor argument to that property
  • Makes it immutable (because val)

This eliminates the boilerplate assignment this.name = name that Java requires.

Creating Instances

kotlin
val p = Person("Bob", 176.0)

val name = p.name           // "Bob"
val lbs = p.weightLbs       // 176.0
val kilos = p.weightKilos   // ~80.0 (176.0 / 2.2)

Kotlin does not use a new keyword — you call the class name as if it were a function.


Constructor Parameters vs Properties

It is worth understanding the difference between a constructor parameter and a property:

  • A parameter with val or var in the constructor header becomes a property — it is accessible on instances via p.name
  • A parameter without val or var is a constructor-only parameter — it exists only during construction and is not accessible afterward

kotlin
class FullName(val firstName: String, val lastName: String, val title: String = "")

kotlin
// title has a default value — it is optional
val person1 = FullName("John", "Doe")
val person2 = FullName("Jane", "Doe", "Dr.")

Default parameter values in constructors eliminate the need for multiple constructor overloads that differ only in optional parameters.


Comparing Kotlin to Java

Consider a simple class that stores a name and a weight with a computed conversion property. In Java:

java
// Java — verbose
public class Person {
    private final String name;
    private double weightLbs;

    public Person(String name, double weightLbs) {
        this.name = name;
        this.weightLbs = weightLbs;
    }

    public String getName() { return name; }

    public double getWeightLbs() { return weightLbs; }
    public void setWeightLbs(double weightLbs) { this.weightLbs = weightLbs; }

    public double getWeightKilos() { return weightLbs / 2.2; }
    public void setWeightKilos(double kilos) { this.weightLbs = kilos * 2.2; }
}

In Kotlin:

kotlin
// Kotlin — concise
class Person(val name: String, var weightLbs: Double) {
    var weightKilos: Double
        get() = weightLbs / 2.2
        set(value) { weightLbs = value * 2.2 }
}

The Kotlin version has fewer lines, less repetition, and expresses the same intent more clearly. Both are fully equivalent in behavior.


Data Classes

For classes that exist purely to hold data — transfer objects, API models, database entities — Kotlin provides data classes:

kotlin
data class User(val id: Int, val name: String, val email: String)

A data class automatically generates:

  • equals() — structural equality based on all properties
  • hashCode() — consistent with equals()
  • toString() — readable representation: User(id=1, name=Alice, email=alice@example.com)
  • copy() — creates a modified copy without mutating the original

kotlin
val user1 = User(1, "Alice", "alice@example.com")
val user2 = user1.copy(email = "newemail@example.com")

println(user1 == user2)  // false (different email)
println(user1.toString()) // User(id=1, name=Alice, email=alice@example.com)

Data classes are ideal for representing data in MVVM architecture — API response models, ViewState objects, and domain entities.


Summary

FeatureDescription
PropertiesData members with optional custom getter/setter logic
val propertyImmutable — cannot be reassigned
var propertyMutable — can be reassigned
Custom getterComputed value derived from backing data
Custom setterConvert or validate before storing
Primary constructorDeclare and initialize properties in the class header
val/var in constructorShorthand that declares a property and assigns it
Data classAuto-generates equals, hashCode, toString, copy

Kotlin classes are designed to be expressive and concise. The primary constructor with inline property declarations eliminates the Java constructor boilerplate, and custom getters and setters provide clean encapsulation without the ceremony of explicit accessor methods.

In Kotlin, a class should say exactly what it needs to say — and nothing more.