Week 1 | Lesson 2

Object-Oriented Programming Principles & Models

Objects, Classes, Data Models, Access modifiers



© 2026 by Monika Protivová

Object-Oriented Programming

What is Object-Oriented Programming?

Object-Oriented Programming (OOP) is a programming paradigm that organizes code around objects
— instances of classes that bundle data and behavior together.

Instead of writing a sequence of instructions (procedural style), OOP lets you model real-world entities and their interactions.

Why is it of interest?

  • Natural to humans — we naturally think in terms of objects, their properties and what they can do (a car has a color and can drive)
  • Models the real world — OOP lets us represent real-world concepts directly in code, making it intuitive to reason about
  • Manages complexity — breaking a large system into objects makes it easier to understand and maintain
  • Promotes reuse — common behavior can be shared across classes through inheritance and composition
  • Widely adopted — most modern languages (Kotlin, Java, Python, C#) support OOP, making it essential for professional development

Object-Oriented Programming

There are four main principles in Object-Oriented Programming ...Object-Oriented Programming ...
  1. Encapsulation
    is a concept of controlling access to the internal state of an object, protecting it from unauthorized access and ensuring data integrity.
  2. Inheritance
    enables a class to have the same behavior as another class by inheriting its properties and methods.
  3. Polymorphism
    allows us to define one interface or method that can have multiple implementations.
    It means that the same method or property could exhibit different behavior in different instances of object implementing given interface.
  4. Abstraction
    is a mechanism to represent the object features without exposing the actual implementation details.
    In other words, user of such object only needs to know what it does, not how it does it.

We will come back to these principles later in the course.

Programming language

and communication of intent

Communication of intent

Programming language provides means of expressing programmer's intent to a computer system.

But programming it is not just a way of giving instructions to a computer. It can also be a means of communication between humans, particularly in the context of team development, code reviews, and future maintenance of the software. Here are few points to keep in mind ...

Code Clarity

Code is more often read than it is written. Therefore, it is important to keep it clean and easily understood.


Code Consistency

Keeping your code consistent in terms of syntax, programming style and design patterns makes it easier to understand.


Documentation and Comments

Some code can become hard to understand despite our best effort. In these cases, comments and code documentation should be used to clarify the programmer's intent or communicate unintuitive information.

With AI adoption, documentation and comments have taken on a whole new meaning:

  • Comments are now prompts — AI coding tools use your comments and documentation as context. Well-documented code leads to better AI-generated suggestions, making comments a direct input to your tooling.
  • The "why" is now the human's job — AI can generate the what (working code), but it cannot generate the why (the reasoning behind a decision). As more code is AI-generated, human-written comments explaining intent become more critical than ever.

Why AI-generated code often lacks intent

AI can produce code that compiles and runs correctly -- but that doesn't mean it communicates well.

What AI is good at

  • Producing syntactically correct code quickly
  • Following common patterns it has seen in training data or in your codebase
  • Generating boilerplate and repetitive structures
  • Following established codebase patterns


What AI doesn't know

  • AI will follow patterns in your codebase - you need to set those pattern
  • AI can't recognize which patterns no longer serve their purpose
  • Why a design decision was made -- only that it was made somewhere before
  • Which parts of the code will change frequently and need to be readable

What this looks like in practice

  • Generic names -- data, result, temp instead of names that reveal purpose
  • Inconsistent style -- mixing patterns from different codebases it was trained on
  • Missing context -- no comments where a human would explain a non-obvious decision
  • Over-engineering -- adding abstractions nobody asked for, because "that's how it's usually done"
❗ ️Your job as reviewer: Code that works is not the same as code that communicates. When you review AI output, ask: would a teammate understand why this code exists, not just what it does?

Objects and Classes

Objects and Classes

We have already seen and worked with Kotlin objects and classes.
Let's explain what they are and how they work in more detail.

What is an object?

Object is a data structure in memory


What is a class?

Class is a template for how to create an object

Think of a class as a blueprint for creating objects.

Class Definition

Class is defined using the class keyword, followed by the class name, class header and the class body.
  • Class name - should begin with an initial letter (capitalized by convention).
  • Class header - class type parameters, the primary constructor, and other things, such as its superclass or interfaces it implements.
  • Class properties - are defined in the class header and are used to hold the state of the class and its objects.
  • Class body - containing fields, methods, constructors, initializer blocks, inner classes, and interfaces enclosed in curly braces.
    • Fields - additional properties that hold the state of the class and its objects.
    • Methods - functions that are part of the class and contain the executable code.
    • Constructors - special methods used to initialize the state of an object.
    • Initializer blocks - unnamed code blocks used for initializing shared variables and executing code that needs to run every time an instance of the class is created.
    • Inner classes and interfaces - class or interface definitions nested within the outer class body.
    • Companion objects - special objects that are tied to a class, rather than to an instance of a class.

Class Definition

Simple class definition without body.
class UniversityCourse
More realistic class definition with parameters and body.
class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate, val students: MutableList<String> = mutableListOf() // initial value of empty list ) { // class field which is not part of the constructor private var isOpen: Boolean = false // initial value of false fun addStudent(studentName: String) { if (isOpen) { students.add(studentName) } else { error("Cannot add students to closed course.") } } fun open() { isOpen = true } fun close() { isOpen = false } }

Class Instantiation

You do not directly work with classes in Kotlin, you work with objects that are created from classes, called class instances.
Object == Instance of a class
  • The process that creates an object from class is called instantiation
  • Instantiation is done by calling a special constructor method
  • Class may have one or more constructors
  • If constructor is not explicitly defined, then the class will default constructor with no arguments
  • Instantiation can be used to set initial values for the object

Class Header

Class header defines its type parameters, the primary constructor, and other things, such it's superclass or interfaces it implements.
  • The class header specifies class properties (fields).
  • Just like functions argument, class fields can have default values.
  • The declared fields also define the class primary constructor.
  • In this example, the class header specifies that the class has four fields, one with a default value, which means that it can be omitted when creating an instance of the class.
    class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate, val students: MutableList<String> = mutableListOf() // initial value of empty list ) { // class body }

Class Constructors

Class constructor is a special method with the only purpose of class instantiation.
Class with zero arguments constructor
class KotlinCourse { val subject: String = "Kotlin" } val kotlinCourse = KotlinCourse()
Class with one arguments constructor
class Course(val subject: String) val kotlinCourse = Course("Kotlin")
Class with multiple arguments constructor
class Course( val subject: String, val startDate: LocalDate, val endDate: LocalDate ) val kotlinCourse = Course( "Kotlin", LocalDate.parse("2024-02-03"), LocalDate.parse("2024-02-21") )
  • Every class has a constructor, even if it is not explicitly defined.
  • Class may have multiple constructors (primary and secondary constructors).
  • Constructor may have zero, one, or many arguments
  • Class constructors support named arguments just like functions.

Alternate class constructors

Kotlin allows you to define multiple constructors for a class, also called secondary constructors. They provide alternative ways to instantiate the class.
Example of class definition, class alternate constructor, and constructor calls.
// primary constructor defined by class header class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate ) { // alternate constructor constructor( subject: String, startDate: String, endDate: String, ) : this(subject, LocalDate.parse(startDate), LocalDate.parse(endDate)) }
Primary constructor call with default values
val kotlinCourse1 = UniversityCourse("Kotlin", LocalDate.parse("2024-02-03"), LocalDate.parse("2024-02-21"))
Alternate constructor call
val kotlinCourse3 = UniversityCourse("Kotlin", "2024-02-03", "2024-02-21")

Default values & Named arguments

Just like functions, class constructors can have default values and support named arguments.
Class constructor really is just a special kind of function, so just like functions, class constructors can have default values and support named arguments.
class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate = startDate.plusDays(30), // default date val students: MutableList<String> = mutableListOf() // default value of empty list )
UniversityCourse("Kotlin", LocalDate.parse("2024-02-03"))

Calling the constructor with default values.
UniversityCourse( "Kotlin", LocalDate.parse("2024-02-03"), students = mutableListOf("Alice", "Bob") )

Calling the constructor with one named argument for students
The endDate will be set to the default value.
UniversityCourse( subject = "Kotlin", startDate = LocalDate.parse("2024-02-03"), endDate = LocalDate.parse("2024-02-21"), students = mutableListOf("Alice", "Bob") )

Naming all arguments when calling the constructor.

Methods

Method is a function associated with an object.
In Kotlin, you can also call methods member functions.

Same rules and possibilities apply to methods as to functions not associated with a class.

class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate = startDate.plusDays(30), val students: MutableList<String> = mutableListOf() ) { fun addStudent(studentName: String) { students.add(studentName) } }

The only difference is that methods are called on an instance of object.

val kotlinCourse = UniversityCourse("Kotlin", LocalDate.parse("2024-02-03")) kotlinCourse.addStudent("Alice") kotlinCourse.addStudent("Bob")

Custom Property Accessors

Kotlin allows you to define custom getters for properties, creating computed properties that derive their value from other properties.

A stored property holds a value in memory. A computed property calculates its value each time it is accessed.

class Rectangle( val width: Double, val height: Double ) { // computed properties — no backing field, calculated on access val area: Double get() = width * height val perimeter: Double get() = 2 * (width + height) val isSquare: Boolean get() = width == height } fun main() { val rect = Rectangle(5.0, 3.0) println("Area: ${rect.area}") // 15.0 println("Perimeter: ${rect.perimeter}") // 16.0 println("Is square: ${rect.isSquare}") // false }

Use computed properties when the value can always be derived from other properties — there is no need to store it separately.

Property Visibility & toString()

Kotlin lets you control who can read or modify a property, and override toString() for a custom string representation.

Use private set to allow reading a property from outside, but only modifying it from inside the class.

Use private var to make a property fully private — not readable or writable from outside.

Override toString() to control how your object looks when printed.

class Counter(val name: String) { var count: Int = 0 private set // readable from outside, but only modifiable inside private var lastOperation: String = "none" // fully private fun increment() { count++ lastOperation = "increment" } override fun toString(): String { return "Counter($name, count=$count)" } } fun main() { val counter = Counter("clicks") counter.increment() counter.increment() println(counter.count) // 2 — reading is allowed // counter.count = 10 // ERROR — setting is not allowed println(counter) // Counter(clicks, count=2) }

Initializer blocks

Initializer blocks are code that is run when an instance of a class is created.

For example, this can be used for additional argument validation or to set up some initial state.

Class may have one or more initializer blocks, which are executed in the order they are defined.

class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate = startDate.plusDays(30), val students: MutableList<String> = mutableListOf() ) { init { require(startDate < endDate) { "End date must be after start date" } } init { println("Course $subject created") } }

Scope and "this" reference

In Kotlin, the this keyword is used to refer to the current instance of a class.
It is often used to distinguish between class properties and parameters or local variables with the same name.
class Person(val name: String) { fun introduce(name: String) { println("${this.name} also goes by $name") } } fun main() { val person = Person("Monika") person.introduce("Moni") // prints "Monika also goes by Moni" }
Additionally, Kotlin provides other scope references using @label to refer to specific instances in nested scopes.
class A { inner class B { fun Int.foo() { val a = this@A // refers to the instance of A val b = this@B // refers to the instance of B val c = this // refers to the receiver Int println("a: $a, b: $b, c: $c") } } }
fun main() { val a = A() val b = a.B() b.run { 42.foo() } }

Class destruction

Java/Kotlin has no class destructor, because freeing up memory is entirely delegated to JVM through a process called garbage collection
Some Java/Kotlin classes may have a special methods that should be called after we are done using the class in order for it to be eligible for garbage collection. I will also mention these later in the course.

Inner classes

Inner class (also called nested class), is a class defined within a body of another class.
class OuterClass { private val outerField: String = "Outer Field" // Inner class inner class InnerClass { fun accessOuterField(): String { return outerField } } } fun main() { val outer = OuterClass() val inner = outer.InnerClass() println(inner.accessOuterField()) // prints "Outer Field" }

Use case for nested classes


Logical Grouping

Nested classes can help us keep our code organized by having related code together. For example, you may want to create an internal data class.


Encapsulation & Access Control

Inner classes can access members of the outer class, including those marked as private. This can benefit us in multiple scenarios, such us when creating helper classes without exposing private methods or fields of the outer class.


Increased Readability and Maintainability

Inner classes are used for code that is relevant to a small part of the outer class. Grouping them together improves code readability and maintainability.

Anonymous Classes

In Kotlin, anonymous classes are a way to create an instance of a class with certain modifications without having to actually declare a new subclass. They are often used to create instances of classes that have no name and are used only once.

Since it has no name, we have no way to instantiate such class.
Thus, an anonymous class must be declared and instantiated with a single expression.

An anonymous class must either implement an interface or extend an existing class (abstract or concrete).

Anonymous classes are useful for creating quick, one-off implementations of interfaces or abstract classes.

fun main() { val runnable = object : Runnable { override fun run() { println("Running in an anonymous class!") } } val thread = Thread(runnable) thread.start() }

Singleton Objects

Static classes and constants in Kotlin

Singleton Objects

Singleton object is a class that can have only one instance in memory.

In Java and other languages, we used so called singleton pattern to create a class that can have only one instance.

Kotlin provides a convenient way to create singleton objects using the object keyword.

object StringUtils { const val DECIMAL_PLACES = 2 fun formatNumber(number: Double): String { return "%.${DECIMAL_PLACES}f".format(number) } } fun main() { val formattedNumber = StringUtils.formatNumber(3.14159) println(formattedNumber) }

Singleton Objects Key Features

True Singleton

Only one instance of the object is created.

Utility Methods

Commonly used for utility methods and constants.

Initialization

The object is initialized when it is first accessed.

No Constructors

Objects cannot have constructors.

Compile-Time Constants

The const val modifier marks a property as a compile-time constant — its value is inlined at compile time.

Use const val for values that are truly constant and known at compile time.

  • Must be initialized with a String or a primitive type (Int, Double, Boolean, etc.)
  • Must be declared at the top level, or inside an object or companion object
  • Cannot have a custom getter
object MathConstants { const val PI = 3.14159265358979 const val E = 2.71828182845905 const val MAX_ITERATIONS = 1000 } class Circle(val radius: Double) { val area: Double get() = MathConstants.PI * radius * radius companion object { const val UNIT_CIRCLE_RADIUS = 1.0 } } fun main() { val c = Circle(Circle.UNIT_CIRCLE_RADIUS) println("Area: ${c.area}") }

Without const, a val is a regular read-only property — it is evaluated at runtime and can hold any type, including objects.

Use case for static objects

Utility or Helper Classes

Sometimes we need some methods to be globally available in the application without needing to create class instance every time. Example of such is utility classes with static methods is String.valueOf() or Integer.toBinaryString().


Global Constants and Variables

Static keyword can be used to define class level variables hat are accessible throughout our application.


Singleton Pattern

Kotlin object is equivalent to Java's singleton pattern, but with the convenience of a simpler, error-proof syntax. Singleton class is a design pattern that restricts the instantiation of a class to a single instance.

Companion Objects

Companion object is an object that is tied to a class, rather than to an instance of a class.
  • There can be only one companion object per class, and it is defined using the companion keyword.
  • It may have a name, but it is optional. Otherwise, it is referred to as a default companion object.
  • It is often used to define static methods and constants.
  • Other than that, it is just like any other object.
  • Companion objects are by convention placed at the bottom of the class.
import java.time.LocalDate class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate ) { // regular class methods companion object UniversityCourseFactory { // companion object name is optional const val KOTLIN = "Kotlin" fun kotlinCourse(startDate: String, endDate: String): UniversityCourse { return UniversityCourse(KOTLIN, LocalDate.parse(startDate), LocalDate.parse(endDate)) } } } fun main() { val kotlinCourse = UniversityCourse.kotlinCourse("2024-02-03", "2024-02-21") println(UniversityCourse.KOTLIN) }

Data Models

Data Classes

Data Models

A data model is a conceptual representation of how data is structured and related in your application.

A data model defines the shape and structure of data in terms of classes, properties, and relationships.

  • In object-oriented programming, data models are typically expressed using classes that reflect real-world entities (e.g., User, Product).
  • A good data model serves as a bridge between:
    • business logic (how the system behaves),
    • data storage (like a database),
    • and the API layer (how other systems communicate with it).

Data Classes

Kotlin provides a special kind of class called a data class to make data modeling more concise and expressive.

For a data class, Kotlin compiler automatically generates convenience methods for copying, comparing, and printing objects and more.

Data classes are marked with the data keyword.

data class UniversityCourse( val subject: String, val startDate: LocalDate, val endDate: LocalDate = startDate.plusDays(30), val students: MutableList<String> = mutableListOf() )

There are a few rules for data classes:

  • The primary constructor must have at least one parameter.
  • Primary constructor must have val or var keyword.
  • Data classes cannot be abstract, open, sealed, or inner.
  • Data classes cannot inherit from other classes.

Data Class Generated Methods

The compiler automatically generates equals(), hashCode(), toString(), and copy() from the primary constructor properties.
data class Student(val name: String, val age: Int) fun main() { val alice = Student("Alice", 21) val alice2 = Student("Alice", 21) // toString() — readable output println(alice) // Student(name=Alice, age=21) // equals() — structural equality (compares property values) println(alice == alice2) // true println(alice === alice2) // false (different instances) // hashCode() — consistent with equals, safe to use as map keys val grades = mapOf(alice to "A") println(grades[alice2]) // A (lookup works because equals + hashCode match) // copy() — create a modified copy (original stays unchanged) val bob = alice.copy(name = "Bob") println(bob) // Student(name=Bob, age=21) println(alice) // Student(name=Alice, age=21) — unchanged }

Destructuring Declarations

Data classes automatically generate componentN() functions, which enable destructuring — extracting properties into separate variables in one step.
data class Student(val name: String, val age: Int) fun main() { val student = Student("Alice", 21) // destructuring — extracts properties by position val (name, age) = student println("$name is $age years old") // Alice is 21 years old // this is equivalent to: val name2 = student.component1() // "Alice" val age2 = student.component2() // 21 // destructuring in a loop val students = listOf( Student("Alice", 21), Student("Bob", 22) ) for ((n, a) in students) { println("$n: $a") } }

The order of variables in destructuring matches the order of properties in the primary constructor, not their names.

Enums

Enums

Enum (enumeration) is a special type of class, which contains a fixed set of constants.
  • Enum is created with the enum class keyword.
  • Enum constants are static and final implicitly.
  • By convention, the enum values should be in upper case.
  • enums can also have properties, methods, and can be initialized with a constructor.
enum class Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } fun main() { val day = Days.MONDAY println(day) // prints "MONDAY" }

Enums

Since enum is a class, it may have fieldsconstructorsmethods
enum class Days(val isWorkDay: Boolean) { MONDAY(true), TUESDAY(true), WEDNESDAY(true), THURSDAY(true), FRIDAY(true), SATURDAY(false), SUNDAY(false); fun isWeekend(): Boolean { return !isWorkDay } fun isWeekday(): Boolean { return isWorkDay } } fun main() { val day = Days.MONDAY println(day) println(day.isWeekday()) }

Enums

Enums are particularly useful for evaluating a finite number of states.

Especially in combination with when expression.

A) Whenever you use when with enums, the compiler will check if all possible values are covered.

B) In case you forget to cover all possible values, the compiler will throw an error. You may also use else branch to cover all other cases.

A
fun getHoursInClass(day: Days): Int { return when (day) { Days.MONDAY, Days.TUESDAY, Days.THURSDAY -> 4 Days.WEDNESDAY, Days.FRIDAY -> 3 Days.SATURDAY, Days.SUNDAY -> 0 } }
B
fun getHoursInClass(day: Days): Int { return when (day) { Days.MONDAY, Days.TUESDAY, Days.THURSDAY -> 4 Days.WEDNESDAY, Days.FRIDAY -> 3 else -> 0 } }

Extension Functions

Extension functions

Extension functions allow adding new methods to existing classes without modifying their source code.

Extension functions are one of the most powerful and popular features of Kotlin. You define an extension function by prefixing the function name with the type you want to extend.

Why do they matter?

As developers, we constantly work with types we didn't create — standard library classes, third-party libraries, framework types. Without extension functions, adding behavior to these types means wrapper classes or utility functions that feel disconnected from the type itself. Extension functions let you write code that reads naturally, as if the capability was always part of the type.

Benefits of extension functions include:

  • Adding new functionality to existing classes which you may not have access to.
  • Using extension functions to create a more fluent API and DSLs.
  • Improving readability and maintainability of code by encapsulating and naming logic in extension functions.
  • Using extension functions to transform objects into other objects.

Extension functions: Examples

Adding new functionality to existing classes which you may not have access to.

For example, you can add a new method to the String class to capitalize the first letter of each word in a sentence.

fun String.capitalizeWords(): String { return this.split(" ").joinToString(" ") { it.capitalize() } }

You can then use this extension function on any String object.

fun String.capitalizeWords(): String { return this.split(" ").joinToString(" ") { it.capitalize() } } fun main() { val sentence = "hello world from kotlin" println(sentence.capitalizeWords()) }

Extension functions: Examples

Using extension functions to create a more fluent API and DSLs.

You can use extension functions to create a more fluent API by adding methods to existing classes that allow you to chain method calls together.

data class Person( val name: String, val age: Int, val country: String ) fun List<Person>.filterByCountry(country: String): List<Person> { return this.filter { it.country.lowercase() == country.lowercase() } } fun List<Person>.sortByName(): List<Person> { return this.sortedBy { person -> person.name } } fun main() { val people = listOf( Person("John", 21, "USA"), Person("Alice", 25, "UK"), Person("May", 30, "Thailand") ) val filteredPeople = people .filterByCountry("Thailand") .sortByName() println(filteredPeople) }

Extension functions: Examples

Improving readability and maintainability of code by encapsulating and naming logic in extension functions.
data class Person( val name: String, val age: Int, val country: String ) fun Person.canDrinkBeer(): Boolean { val legalAge = when (country) { "USA" -> 21 else -> 18 } return age >= legalAge } fun main() { val person = Person("John", 21, "USA") println("${person.name} can drink beer: ${person.canDrinkBeer()}") }

Extension functions: Examples

Using extension functions to transform objects into other objects.
import java.time.LocalDate data class Person( val name: String, val age: Int, val country: String ) data class Student( val name: String, val country: String, val dateEnrolled: LocalDate ) fun Person.toStudent() = Student( name = name, country = country, dateEnrolled = LocalDate.now() ) fun main() { val person = Person("John", 21, "USA") val student = person.toStudent() println(student) }

Pairs and Triples

Additional Data Types

Pairs and Triples

Kotlin SDK provides two classes to represent pairs and triples of values.

While you can implement your own classes to represent pairs and triples of values, it is convenient to use the standard library classes Pair and Triple as they already provide useful functionality.

Pairs and triples are useful when you need to return two or three values from a function and are quite commonly used when working with collections.

Pair

Pair is a class that holds two values of same or different types.
val duel: Pair<String, String> = Pair("Luke Skywalker", "Darth Vader")

As usual, type can be omitted if it can be inferred from the values.

val firstInEpisode = Pair("Luke Skywalker", 4)

You can access values of a pair by using the first and second properties.

val good = duel.first val evil = duel.second

Or you can use destructuring declarations to extract values from a pair.

val (good, evil) = duel

There is alternative (and preferred) syntax for creating pairs using the to

val duel = "Luke Skywalker" to "Darth Vader"

Triple

Triple
val firstDuelInEpisode = Triple("Luke Skywalker", "Darth Vader", 4)

You can access values of a triple by using the firstsecondthird

val good = firstDuelInEpisode.first val evil = firstDuelInEpisode.second val episode = firstDuelInEpisode.third

Or you can use destructuring declarations to extract values from a triple.

val (good, evil, episode) = firstDuelInEpisode

There is no alternative syntax for creating triples using the toIn this example, it would result in Pair<Pair<String, String>, Int>

val firstDuelInEpisode: Pair<Pair<String, String>, Int> = "Luke Skywalker" to "Darth Vader" to 4

Packages, Imports and Modifiers

Packages

Packages are used to organize code into namespaces, making it easier to manage and avoid naming conflicts.
  • The package declaration is usually the first line in a Kotlin file. It specifies the package to which the file belongs.
  • Package names are typically written in all lowercase and follow the reverse domain name convention.
  • To use classes and functions from other packages, you need to import them using the import keyword.
  • If no package is specified, the file belongs to the default package.

package com.motycka.edu.model // <- package name import java.time.LocalDate // <- import of class LocalDate from java.time package class User( val name: String, val birthDate: LocalDate )

Modifiers

Modifiers in programming languages are keywords that you can use to change the properties or behavior of classes, methods, and variables.

They can be broadly categorized into two types:


Access Modifiers

These define the visibility or accessibility of functions, classes, methods, and variables.


Non-Access Modifiers

These define other characteristics such as behavior, state, or implementation details.

Access Modifiers

Access, modifiers define the visibility or accessibility of functions, classes, methods, and variables.
modifier on class on method on field
public accessible from anywhere accessible from anywhere accessible from anywhere
private only accessible within the same package only accessible within same class only accessible within same class
protected accessible within the same package only accessible within same class or it's subclass only accessible within same class or it's subclass
internal accessible within the same module accessible within the same module N/A
default same as public same as public same as public

Non-Access Modifiers

Non-access modifiers define other characteristics such as behavior, state, or implementation details.
modifier on class on method / block on field
abstract Class marked as abstract cannot be directly instantiated. Method marked as abstract does not provide implementation, but expects a subclass to implement it. N/A
final prevents inheritance prevents method overloading Makes variable a constant = value cannot be changed after initialization.
open Allows class to be inherited Allows method to be overridden Allows property to be changed
override N/A Indicates that a method is overriding a method in a superclass. N/A
lateinit N/A Indicates that a property will be initialized later. N/A
const N/A N/A Indicates that a property is a compile-time constant.
companion N/A N/A Defines a companion object, which is an object that is tied to a class and can access its private members.