Skip to content
Share
Explore

KOTLIN Coding Drills Lab Book

Pillars of Object Orientation:

Day 02 Learning Outcomes

megaphone

Introduction to Object Oriented Programming

Defining a class
Declaring class properties
The “this” keyword
Declaring class functions : methods or operations that the class performs
Constructors : special method that you run instantiate an object from a class
Creating an instance of a class : what you do when you run the constructor
Class → Recipe, set of assembly instructions
OBJECT → cake : the realized version o your design

class Person {
var name: String = ""
var age: Int = 0

// Method to print info with some color
fun printInfo() {
println("\u001B[34mName: $name, Age: $age\u001B[0m")
}

// Method to celebrate a birthday with a fun message
fun celebrateBirthday() {
this.age += 1
println("\u001B[33mHappy Birthday, ${this.name}! You are now ${this.age} years old.\u001B[0m")
}

// Method for a custom greeting
fun greet() {
println("\u001B[32mHello, $name! Welcome to our community!\u001B[0m")
}

// Method to display a brief life story
fun lifeStory() {
println("\u001B[35m$name's Life Story: \n$name was born $age years ago. Over the years, $name has experienced many adventures and learned a lot. Now, at the age of $age, $name is excited about the future!\u001B[0m")
}
}

fun main() {
val person1 = Person().apply {
name = "Alice"
age = 25
}

val person2 = Person().apply {
name = "Bob"
age = 30
}

person1.printInfo()
person1.celebrateBirthday()
person1.greet()
person1.lifeStory()

person2.printInfo()
person2.celebrateBirthday()
person2.greet()
person2.lifeStory()
}

megaphone

The `apply` function in Kotlin is a scope function that allows you to configure an object within a block of code and then return the object itself. It’s particularly useful for initializing or configuring an object without having to reference the object’s name multiple times.

### What `apply` is Doing
In your code, `apply` is being used to set the properties `name` and `age` of `Person` objects in a more concise way. Here’s a breakdown of how it works:
1. **Create a new `Person` object**. 2. **Call `apply` on the newly created object**. 3. **Inside the `apply` block, refer to the object using `this` implicitly** (so you don’t need to use the object’s name to set its properties). 4. **Return the object itself after the block is executed**.
### Example Code with Explanation
```kotlin class Person { var name: String = "" var age: Int = 0
fun printInfo() { println("Name: $name, Age: $age") }
fun celebrateBirthday() { this.age += 1 println("Happy Birthday, ${this.name}! You are now ${this.age} years old.") }
fun greet() { println("Hello, $name! Welcome to our community!") }
fun lifeStory() { println("$name's Life Story: \n$name was born $age years ago. Over the years, $name has experienced many adventures and learned a lot. Now, at the age of $age, $name is excited about the future!") } }
fun main() { val person1 = Person().apply { name = "Alice" age = 25 }
val person2 = Person().apply { name = "Bob" age = 30 }
person1.printInfo() person1.celebrateBirthday() person1.greet() person1.lifeStory()
person2.printInfo() person2.celebrateBirthday() person2.greet() person2.lifeStory() } ```
### What Happens in Detail
1. **Creating `person1`**: ```kotlin val person1 = Person().apply { name = "Alice" age = 25 } ``` - A new `Person` object is created. - `apply` is called on this object. - Inside the `apply` block, `name` is set to "Alice" and `age` is set to 25. - The `apply` function returns the `Person` object, now initialized with `name` and `age`.
2. **Creating `person2`**: ```kotlin val person2 = Person().apply { name = "Bob" age = 30 } ``` - Similar steps as above, but for a different `Person` object with `name` set to "Bob" and `age` set to 30.
3. **Calling Methods on `person1` and `person2`**: - `printInfo()`, `celebrateBirthday()`, `greet()`, and `lifeStory()` methods are called on both `person1` and `person2` to demonstrate their initialized state and behavior.
### Why Use `apply`
- **Readability**: It makes the code more readable by reducing the repetition of the object’s name. - **Convenience**: It allows you to perform multiple operations on the object in a single block of code.
### Example without `apply`
If you didn’t use `apply`, you would have to write more verbose code:
```kotlin val person1 = Person() person1.name = "Alice" person1.age = 25
val person2 = Person() person2.name = "Bob" person2.age = 30
person1.printInfo() person1.celebrateBirthday() person1.greet() person1.lifeStory()
person2.printInfo() person2.celebrateBirthday() person2.greet() person2.lifeStory() ```
Using `apply` simplifies this process, making the initialization and configuration more compact and readable.

Here are six progressive lab drills designed to teach you the various aspects of defining a Kotlin class.
Each drill builds on the previous one, gradually introducing new concepts and complexity.
info
These lab drills cover:
Basic class definition and properties.
Primary constructors.
Secondary constructors.
Custom methods.
Default values and initialization blocks.
Data classes and additional methods.
By following these steps, students will progressively understand how to define and work with classes in Kotlin, gaining a solid foundation in object-oriented programming principles.

Lab 1: Basic Class Definition

Objective: Introduce the basic structure of a Kotlin class, including properties and a simple method.
Instructions:
Define a class Person with two properties: name (String) and age (Int).
Create a method printInfo() to print the name and age.
Code:

class Person {
var name: String = ""
var age: Int = 0

fun printInfo() {
println("Name: $name, Age: $age")
}
}

fun main() {
val person = Person()
person.name = "John"
person.age = 25
person.printInfo()
}


Lab 2: Primary Constructor

Objective: Introduce the use of a primary constructor to initialize properties.
Instructions:
Modify the Person class to use a primary constructor for initializing name and age.
Update the printInfo() method accordingly.
Code:

class Person(val name: String, var age: Int) {

fun printInfo() {
println("Name: $name, Age: $age")
}
}

fun main() {
val person = Person("Alice", 30)
person.printInfo()
}


Lab 3: Secondary Constructor

Objective: Introduce the use of a secondary constructor for additional initialization logic.
Instructions:
Add a secondary constructor to the Person class that only takes name and sets age to a default value.
Demonstrate using both constructors.
Code:

class Person(val name: String, var age: Int) {

constructor(name: String) : this(name, 0)

fun printInfo() {
println("Name: $name, Age: $age")
}
}

fun main() {
val person1 = Person("Bob", 40)
person1.printInfo()

val person2 = Person("Charlie")
person2.printInfo()
}


megaphone

More play with secondary constructors

image.png
To execute specific code when the secondary constructor is called in Kotlin, you can include the additional logic within the secondary constructor block.
Let's modify thePerson class to demonstrate how this can be done.

Step-by-Step Explanation

Define the Primary Constructor: The primary constructor initializes the name and age properties.
Define the Secondary Constructor: The secondary constructor initializes the name property and includes additional logic specific to this constructor.
Call the Primary Constructor: The secondary constructor must call the primary constructor using the this keyword.

Modified Code Example

kotlinCopy codeclass Person(val name: String, var age: Int) {
// Primary constructor constructor(name: String) : this(name, 0) { println("Primary constructor called for $name with default age 0") }
// Secondary constructor constructor(name: String, vacayPreference: String) : this(name) { println("Secondary constructor called for $name with vacation preference: $vacayPreference") // Additional logic specific to the secondary constructor if (vacayPreference.isNotEmpty()) { println("$name prefers vacationing in $vacayPreference.") } }
fun printInfo() { println("Name: $name, Age: $age") }}
fun main() { val person1 = Person("Bob", 40) person1.printInfo()
val person2 = Person("Charlie") person2.printInfo()
val person3 = Person("Mike", "Hawaii") person3.printInfo()}

Explanation of the Code

Primary Constructor:
Initializes name and age.
Additional logic can be added here if needed (e.g., default values).
Secondary Constructor:
Calls the primary constructor using this(name).
Adds specific logic for the secondary constructor, such as handling the vacayPreference parameter.
Prints additional messages or performs other actions specific to this constructor.
Main Function:
Creates instances using different constructors to demonstrate both primary and secondary constructors.
Calls printInfo() to display the initialized values and any additional messages.

Output

Running this code will produce the following output:
sqlCopy codePrimary constructor called for Bob with default age 0Name: Bob, Age: 40Primary constructor called for Charlie with default age 0Name: Charlie, Age: 0Primary constructor called for Mike with default age 0Secondary constructor called for Mike with vacation preference: HawaiiMike prefers vacationing in Hawaii.Name: Mike, Age: 0
This output shows that the secondary constructor adds specific behavior for the vacayPreference parameter while still initializing the object through the primary constructor.

Lab 4: Custom Methods

Objective: Add custom methods to demonstrate how we can perform operations on classes.
Instructions:
Add a celebrateBirthday() method to the Person class to increase the age by 1 and print a birthday message.
Update the main() function to demonstrate this method.
Code:

class Person(val name: String, var age: Int) {

constructor(name: String) : this(name, 0)

fun printInfo() {
println("Name: $name, Age: $age")
}
Want to print your doc?
This is not the way.
Try clicking the ··· in the right corner or using a keyboard shortcut (
CtrlP
) instead.