Classes and objects.

We have learned about data types and functions. Now we combine them. Classes let us create our own types that bundle data and behavior. Today we learn to define and use classes in Kotlin.

A class is a blueprint for creating objects.

It defines what data the objects hold. It defines what operations the objects support.

Think of a class as a template. The class Person might specify: every person has a name and an age. The class also might define: persons can introduce themselves.

An object is an instance of a class. You use the class to create actual persons: Alice, age 30. Bob, age 25. Each is an object. Each follows the Person blueprint.

Classes let you model concepts from your domain. Users. Products. Orders. Each becomes a class. Each instance is a specific user, product, or order.

Declare a class with the class keyword.

class Person with empty braces. This is the simplest class. It exists but has no properties or methods.

Add properties to store data: class Person with var name String and var age Int.

Add functions to define behavior: class Person with a function introduce that prints "I am" name.

Create an instance with the class name followed by parentheses: val alice equals Person parentheses.

Access properties with dot notation: alice dot name equals "Alice". alice dot age equals 30.

Call methods with dot notation: alice dot introduce parentheses.

Most classes need initial values. The primary constructor provides them.

The primary constructor is part of the class header: class Person with val name String and val age Int.

This creates a class with two read-only properties. Every Person must be created with a name and age.

Create instances by passing arguments: val alice equals Person "Alice", 30.

The val keyword makes properties immutable. Use var if the property should be changeable.

Parameters without val or var are just constructor parameters. They are not properties. They disappear after construction.

The primary constructor is concise. Most classes use it for their properties.

Sometimes you need to run code during construction.

An init block runs after the primary constructor: class Person with val name String, then init block that prints "Created" name.

You can have multiple init blocks. They run in order, interleaved with property initializers.

Use init for validation: init with require that name is not blank.

Use init for computed initial values: class Rectangle with width and height, then init that sets area equals width times height.

Keep init blocks simple. Complex initialization logic might indicate a need for factory functions.

Sometimes you need multiple ways to create an object.

Secondary constructors use the constructor keyword.

class Person with val name and val age, then constructor with just name that calls this with name and zero.

This adds a second way to create a Person: just with a name. The secondary constructor must call the primary constructor using this.

Secondary constructors can do additional setup. They can have a body.

In practice, default parameter values often eliminate the need for secondary constructors. class Person with name and age equals 0 achieves the same.

Use secondary constructors when default values are insufficient.

Properties are more than variables. They can have custom getters and setters.

val creates a read-only property. It has a getter but no setter.

var creates a mutable property. It has both getter and setter.

Custom getter: var name String, get returns field dot uppercase. This transforms the value when read.

Custom setter: var name String, set value, field equals value dot trim. This transforms the value when written.

The field keyword refers to the backing field. This is where the actual value is stored.

Use custom accessors for validation, transformation, or lazy computation.

Many classes are just containers for data. Kotlin has a special form for these.

data class Person with val name String and val age Int.

The data keyword generates useful methods automatically.

equals compares all properties. Two persons with the same name and age are equal.

hashCode is consistent with equals. Data classes work correctly in sets and maps.

toString provides a readable representation: Person name equals Alice, age equals 30.

copy creates a modified copy: alice dot copy with age equals 31.

Data classes are perfect for value objects. Use them when identity is defined by the data, not by reference.

Sometimes you need exactly one instance. Not a class, but a singleton.

object Configuration with val maxUsers equals 100 and val appName equals "MyApp".

This creates a single object named Configuration. There is no class. Just one instance.

Access it directly: Configuration dot maxUsers.

Object declarations are useful for global state, configuration, or utility functions.

Sometimes you need static-like members in a class.

Kotlin does not have static keyword. Instead, use companion objects.

class Person with val name String, then companion object with function create that returns Person with that name.

The companion object belongs to the class, not to instances.

Call it through the class name: Person dot create "Alice".

Use companion objects for factory methods, constants associated with the class, or anything that should exist once per class, not once per instance.

Let us summarize.

Classes are blueprints for objects. They define properties and methods.

Primary constructors define properties concisely.

Init blocks run code during construction.

Data classes auto-generate equals, hashCode, toString, and copy.

Object declarations create singletons.

Companion objects provide class-level members.

Classes let you model your domain with custom types.