Share
Explore

w24 MADS 4013 March 7 Kotlin Object-Oriented Programming


Today’s Topics:

Setters and Getters
- Usage case for Mutators: Purpose to state Object state integrity: Another method cannot change the data state of the OBJECT.
What is OBJECT STATE: state of an object =the values of the OBJECT FIELDS at any point in time.
Data variables → now in OO these are referred to as FIELDS.
2 members that an OBJECT can have are:
Data Fields
Methods
In OO language: The purpose of an OBJECT is “tightly bind” the fields in the OBJECT with the METHODS
Object is a “Walled Garden” : Object oriented principle of Encapsulation says that we should make our Data Fields PRIVATE meaning NOT available to be read or changed OUTSIDE the OBJECT except by Getter and Setter Methods
Purpose of this? To protect the state integrity of the OBJECT by ensuring that data fields can be changed ONLY by method calls!
In software engineering: It is easy to control and audit method calls.
Whereas monitoring direct access to data fields - Not easily traceable.
In KOTLIN: 2 kinds of mutators —
Explicit : we write the method to change the variable - we can put additional business logic in the Mutator method to log or other condition/process/allow or deny that read or change event.
Implicit - Dot accessor notation.

Learning Outcomes:

Set or accessor variables
The private keyword and how to access the private variable from outside the class
Understanding the Operation of Implicate and Explicit Accessors and Mutators on Private Data Fields in the KOTLIN OBJECT

Part 1 of today’s class: Introducing setters and getters on private data fields:

These can be either IMPLICIT or Explicit:

info

Understanding Getters and Setters in Kotlin

Kotlin, a modern programming language, aims at boosting productivity, readability, and safety.
One of the mechanisms KOTLIN employs to implement the OO principle of encapsulations through properties, which encapsulate fields: access only via getters, and setters. Understanding the nuances between explicit and implicit getters/setters in Kotlin is essential for crafting clean, efficient, and robust applications.
Explicit vs. Implicit Accessors and Mutators
#### Explicit Accessors and Mutators
1. **Definition**: Explicit accessors (getters) and mutators (setters) in Kotlin are those you define manually in the class for a property.
They allow you to add custom behavior when a property is accessed or modified.
2. Usage Scenario**: They are particularly useful when you need to perform validation, logging, or other operations beyond merely setting or getting a property's value.
3. **Implementation: Explicit setters are defined with functions within the property declaration. They give you full control over how and what values can be assigned to a field.
4. **Example: ```kotlin var name: String = "default" get() = field set(value) { field = value.trim() } ``` Here, the setter trims the whitespace from the `name` before setting its value.

Implicit Accessors and Mutators

1. **Definition: Implicit accessors and mutators are automatically generated by Kotlin for properties. Kotlin does this to simplify code and enhance readability.
2. **Usage Scenario: They are used when no additional processing is required during property access or mutation. This makes property declaration concise and straightforward.
3. **Implementation: Whenever you declare a property with `var` or `val` and do not provide a custom getter or setter, Kotlin implicitly provides them.
Access to the property or its mutation is done using the dot accessor notation.
4. Example: Dot accessor notation ```kotlin var country: String = "Unspecified" ``` In this case, Kotlin automatically generates a default getter and setter for `country`. Access and mutation are straightforward: `instance.country` or `instance.country = "Canada"`.

Preferences: When to Use Which?

1. Explicit over Implicit:
If the logic for accessing or mutating a property goes beyond just retrieving or updating its value — such as validation, formatting, or logging — explicit getters and setters are preferred. This custom logic ensures that any access or modification adheres to certain rules or conditions. ​2. Implicit for Simplicity:
When a property does not require any additional handling and is simply meant to hold a value, implicit getters and setters provided by Kotlin are the ideal choice.
This approach keeps the code cleaner and more readable.

Functions and Dot Notation

1. **Explicit Setters with Functions**:
When we talk about explicit setters being done with functions, it refers to defining a `set(value)` block within the property declaration.
This is not a separate function but a component of the property structure that influences how values are set. 2. Implicit Mutators and Dot Accessor Notation: Implicit mutators, ie., setters generated by Kotlin, are utilized through dot notation.
This is because the language abstracts away the function-like behavior of setting a value, making it appear as if you're directly accessing the property.

Conclusion

In summary, the choice between explicit and implicit getters and setters in Kotlin hinges on the specific requirements of your property access and mutation logic.
Explicit accessors and mutators offer the flexibility necessary for more complex operations, while implicit ones excel in simplicity and brevity for straightforward property management.
Understanding this distinction enhances your toolkit for writing concise, maintainable, and effective Kotlin code.
Below is a Kotlin application that demonstrates the use of classes, objects, and private data fields with implicit access and modification through methods (getters and setters) and constructor parameters.
The example includes a Book class with private properties and a Library class to manage a collection of books.

Kotlin Application: Library Management

// Book class with private properties and a constructor to initialize the book details class Book(title: String, author: String, var year: Int) {
private var title: String = title // Getter for title. No explicit call needed, called automatically when accessing the property. get() = field
private var author: String = author // Getter for author. No explicit call needed, called automatically when accessing the property. get() = field
// Function to display book details, indirectly accessing private properties fun displayBookInfo() { println("Book: \"$title\" by $author, published in $year") }
// Example of a method to update the year, demonstrating mutation of a private field fun updateYear(newYear: Int) { if (newYear > 0) { year = newYear } else { println("Invalid year. Please provide a valid year.") } } }
// Library class managing a collection of books class Library { private val books = mutableListOf<Book>()
// Function to add a book to the library fun addBook(book: Book) { books.add(book) }
// Function to display all books in the library fun displayAllBooks() { for (book in books) { book.displayBookInfo() } } }
// Main function to demonstrate the application usage fun main() { // Creating instance of Library val library = Library()
// Creating book instances val book1 = Book("The Kotlin Programming Language", "JetBrains", 2016) val book2 = Book("Effective Java", "Joshua Bloch", 2001)
// Adding books to the library library.addBook(book1) library.addBook(book2)
// Displaying all books in the library library.displayAllBooks()
// Updating the year of a book book2.updateYear(2008) println("After updating the year:") library.displayAllBooks() }
megaphone
In the Kotlin Application for Library Management provided, data fields are marked as private in a structured manner ensuring encapsulation and secure data manipulation. Here's how privacy is established and maintained:
### Book Class
1. **Private Properties**: - The `Book` class declares two primary properties, `title` and `author`, as private variables. This encapsulation ensures that these properties can only be accessed or mutated within the class itself, thereby safeguarding the integrity of the data. - Additionally, the constructor parameters are directly assigned to these private properties, which demonstrates a common pattern in Kotlin for initializing an object's state.
2. **Getters for Private Fields**: - For both `title` and `author`, implicit getters are defined (`get() = field`). This is an idiomatic approach in Kotlin to allow controlled access to the private fields. Even though the keyword `field` refers to the backing field of the properties, outside access is strictly controlled through these getters.
3. **Implicit vs. Explicit Access**: - The `year` property is public, allowing for direct access and modification. However, it maintains the private status of `title` and `author`, illustrating a mixed approach to property visibility based on the necessity of external access and modification.
### Library Class
1. **Private Collection**: - The `Library` class contains a private mutable list of `Book` instances (`private val books`). This encapsulation ensures that the list can only be directly manipulated through the methods provided by the `Library` class itself, such as `addBook()` and `displayAllBooks()`. It prevents external entities from directly modifying the collection, fostering data integrity and encapsulation.
### Access and Modification Patterns
1. **Indirect Data Access**: - The `displayBookInfo()` method in the `Book` class and the `displayAllBooks()` method in the `Library` class demonstrate how encapsulated data can be accessed indirectly. These methods provide read-only access to the data for external use without compromising the private status of the properties.
2. **Controlled Mutation**: - The `updateYear(newYear: Int)` method in the `Book` class offers a controlled way to modify the `year` property. It includes logic to ensure that only valid years are assigned, illustrating how encapsulated data can be safely mutated within predefined constraints.
### Conclusion
The program smartly uses Kotlin's encapsulation features to define private data fields, ensuring that data is both protected and accessible in a controlled manner. This design pattern promotes cleaner, safer, and more maintainable code by allowing class methods to manage how their properties are accessed and modified, preventing incorrect usage or unintended side effects. This Kotlin application provides a straightforward example of encapsulation—access and modification of private properties in the Book class are done implicitly through publicly accessible methods displayBookInfo and updateYear.

In Kotlin, fields (properties) can be enforced to be private through the use of visibility modifiers. The primary purpose of enforcing fields to be private is to encapsulate the data, allowing control over how it's accessed or modified from outside of the class.
Here's how Kotlin enables this enforcement:
Using `private` Keyword
1. **Direct Declaration**: - When declaring a field within a class, prefixing it with the `private` keyword restricts access to the field within the class it's declared in. - Example: ```kotlin class Book { private var title: String = "Untitled" } ``` Here, the `title` field cannot be accessed directly outside the `Book` class.
Here is the engine of how encapsulation works:
private variables : not accessible outside the class / accessible only to other variables and methods in the Class
I can now make a PUBLIC method which can access private fields in the OBJECT: calling that PUBLIC method will through one level of indirection yield controllable access to the field.

2. Constructor Properties:

- Kotlin allows defining properties directly in the constructor.
Marking these properties as `private` in the constructor restricts their visibility outside the class. - Example: ```kotlin class Book(private val author: String) In this case, `author` can only be accessed within the `Book` class.
Implications of Private Fields
- Accessing Private Fields: Private fields can be accessed within the class they are declared in. This includes all functions and initializers within the class. - Encapsulation: By making fields private, a class can hide its internal data and state from other classes. This is a fundamental principle of object-oriented programming, promoting encapsulation.
- Controlled Access: Often, even though a field is private, a class can provide controlled access to it through public methods.
For instance, a class might expose a public getter for a private field but not a setter, effectively making the field read-only from an external viewpoint.
Getter and Setter for Private Fields
- Custom Getters and Setters: Kotlin allows defining custom accessors for properties. A private property can have a public getter to allow read access from outside the class, or a public setter to control how its value is modified. - Example: ```kotlin class Book { private var _yearPublished: Int = 0 val yearPublished: Int get() = _yearPublished } Here, `_yearPublished` is a private field, but its value can be accessed externally through the public `yearPublished` property.

Modularity and Clean Architecture
- By enforcing fields to be private, Kotlin helps developers build a more modular and clean architecture. It encourages the separation of concerns by allowing classes to manage their own state and exposing only what's necessary to the outside world.
### Conclusion
Kotlin's visibility modifiers, particularly the `private` keyword, are straightforward yet powerful tools in enforcing field privacy. They play a crucial role in data encapsulation, ensuring that a class's fields are only accessible and modifiable in controlled ways. This promotes a more secure, maintainable, and encapsulated codebase, adhering to object-oriented design principles [citation:1][citation:3].

Additionally, the Library class demonstrates managing a collection of objects. The application showcases how Kotlin's classes, objects, and properties can be used to construct a simple yet effective program structure.

In the provided Kotlin application, which serves as a library management system, implicit accessors and mutators, better known as getters and setters, are utilized for handling private data fields. Here’s a detailed breakdown of their usage:

### Implicit Getters for Private Properties - Title and Author in the Book Class: The `Book` class defines `title` and `author` as private properties and then explicitly declares getters for them. While it might seem at first glance that these getters are explicit due to their declaration, the key point is that their implementation (`get() = field`) adheres to Kotlin’s default behavior for getters. This means they are implicitly called whenever `title` and `author` are accessed within the class, like in the `displayBookInfo()` method. In this case, the explicit declaration is purely syntactical, as it does not alter the default behavior; hence, it is considered utilising implicit access mechanisms provided by Kotlin. The reason for this approach is to maintain encapsulation while still allowing internal class methods to access private properties in a controlled manner [citation:5].
- **Year Property:** Although `year` is publicly mutable within the class scope due to its default public setter, it also implicitly uses the default getter when read, similar to `title` and `author`. However, since `year` does not have an explicit getter defined in the code, its implicit accessor is utilized directly whenever the property is accessed, including within the `displayBookInfo()` method.
Implicit Mutator for Public Property - **Year Property Mutation:** The `year` property of the `Book` class, besides having an implicit getter, also uses an implicit setter generated by Kotlin. This setter is invoked in the `updateYear(newYear: Int)` method, allowing the mutation of the `year` property from within the class. This implicit setter is part of Kotlin’s property system, where a public `var` property automatically receives a default setter. The explicit check (`if (newYear > 0)`) before assignment demonstrates adding custom logic around property mutation while still leveraging the built-in setter for the actual assignment.
The use of implicit accessors and mutators in Kotlin simplifies the syntax and reduces boilerplate code, as accessors are automatically generated by the compiler for properties. This leads to cleaner and more readable code. In the context of this library management system, it ensures that encapsulation is maintained for `title` and `author` by restricting direct external access while still allowing these properties to be read through controlled means within the class. For `year`, it provides a quick and standardized way to read and write the property value, demonstrating Kotlin’s flexibility in property management [citation:2][citation:4][citation:5].
The design decision to make `year` publicly mutable (or at least mutable within the class scope), compared to keeping `title` and `author` with private access and only read capability through getters, reflects the specific requirements or logic of the application: `year` might need to be updated post-instantiation, unlike `title` and `author` which, following this design, are considered immutable once a `Book` object is created. This showcases a thoughtful application of Kotlin’s property system tailored to the domain logic of the library management system.

Demonstrate explicit accessing and mutating of private data fields in Kotlin,

Let’s write an application that models a school system.
This scenario includes three classes: Student, Teacher, and Course.
Each class will have private fields with explicitly defined setters and getters for accessing and mutating their values.
This Kotlin application is crafted to provide a clear understanding of OOP principles, focusing on encapsulation and the explicit handling of class properties.
This program thoroughly adheres to object-oriented programming principles:
- by defining a set of related classes that model a simplistic school system. Each class ensures encapsulation by keeping its fields private and providing explicit getter and setter methods for field access and mutation, in line with Kotlin's support for both functional and object-oriented programming styles 1 .
/** * Class representing a Student. * Contains private fields that can only be accessed and mutated through explicit methods. */ ​class Student(private var name: String, private var age: Int) {
// Getter method for the name field fun getName(): String { return name }
// Setter method for the name field fun setName(newName: String) { name = newName }
// Getter method for the age field fun getAge(): Int { return age }
// Setter method for the age field, with an input validation fun setAge(newAge: Int) { if(newAge >= 5) { age = newAge } else { println("Invalid age. Age must be 5 or older.") } } // Function to display student information fun displayInfo() { println("Student Name: $name, Age: $age") } }
/** * Class representing a Teacher. * Demonstrates Kotlin's capability for handling private properties explicitly. */ class Teacher(private var name: String, private var subject: String) {
// Getter for the name property fun getName(): String { return name }
// Setter for the name property fun setName(newName: String) { name = newName }
// Getter for the subject property fun getSubject(): String { return subject }
// Setter for the subject property fun setSubject(newSubject: String) { subject = newSubject }
// Method to display teacher's information fun displayInfo() { println("Teacher Name: $name, Subject: $subject") } }
/** * Class representing a Course. * It utilizes explicit method calls to interact with private properties of objects. */ class Course(private var courseName: String, private var teacher: Teacher) { private val students = mutableListOf<Student>()
// Adds a student to the course fun enrollStudent(student: Student) { students.add(student) }
// Displays course information along with the enrolled students fun displayCourseInfo() { println("Course Name: $courseName, Taught by: ${teacher.getName()}") println("Enrolled Students:") students.forEach { student -> student.displayInfo() } } }
/** * The main entry point for the application, showcasing the usage of our classes. */ fun main() { // Creating Teacher and Student objects val msSmith = Teacher("Ms. Smith", "Mathematics") val john = Student("John Doe", 14) val jane = Student("Jane Doe", 13) // Creating a Course object val mathCourse = Course("Algebra 1", msSmith) // Enrolling students in the course mathCourse.enrollStudent(john) mathCourse.enrollStudent(jane)
// Displaying course information mathCourse.displayCourseInfo() }
image.png
This Kotlin program effectively demonstrates the use of explicit getters and setters for accessing and mutating private variables within classes. Below is a detailed explanation highlighting how explicit getters and setters are utilized across different classes in the program:
### Student Class 1. **Private Variables:** The `Student` class declares two private variables, `name` and `age`, which cannot be accessed directly from outside the class. 2. **Explicit Getters:** - `getName()`: Returns the value of the private `name` variable. - `getAge()`: Returns the value of the private `age` variable. These methods allow external access to the private variables indirectly. 3. **Explicit Setters:** - `setName(newName: String)`: Accepts a `String` parameter and assigns it to the `name` variable, effectively mutating its value. - `setAge(newAge: Int)`: Accepts an `Int` parameter and assigns it to the `age` variable after validating the age to be 5 or above, showcasing conditional mutation of private variables. 4. **Usage:** Within `displayInfo()`, it internally accesses these variables directly, a privilege not available outside the class scope.
### Teacher Class 1. **Private Variables:** Similar to the `Student` class, it holds `name` and `subject` as private variables. 2. **Explicit Getters:** - `getName()`: Returns the `name` of the teacher. - `getSubject()`: Returns the `subject` taught by the teacher. 3. **Explicit Setters:** - `setName(newName: String)`: Sets the teacher's `name`. - `setSubject(newSubject: String)`: Sets the `subject` taught by the teacher. 4. **Functionality:** Through `displayInfo()`, the class internally showcases the ability to utilize these getters for presenting information—demonstrating the interaction with private properties via their explicit accessors.
### Course Class 1. **Private Variables:** Holds `courseName` and `teacher` as private. It also maintains a private list, `students`, illustrating the use of a collection as a private field. 2. **Getter for Teacher:** Although not explicitly defined like in `Student` and `Teacher`, it employs `teacher.getName()` to access the teacher's name indirectly via the Teacher's explicit getter method when displaying course information. 3. **List Manipulation:** The method `enrollStudent(student: Student)` indirectly manipulates the `students` private list by adding student objects to it, showcasing another form of indirect data mutation.
### Main Function - The main function ties all classes together, demonstrating how the encapsulated entities interact. It creates instances of `Teacher`, `Student`, and `Course`, using explicit setters to mutate private fields and explicit getters to access the information for display purposes. The `displayCourseInfo()` in the `Course` class aggregates data accessed via getters to present comprehensive course details.
This method of using explicit getters and setters achieves data encapsulation and integrity by controlling how private fields are accessed and mutated. It allows for additional logic (like validation) to be incorporated easily into the data access workflow, providing a clear structure for data management within classes.



megaphone

Setters and Getters - The private keyword and how to access the private variable from outside the class


error

Introduction to Kotlin Setters and Getters

Learning Outcomes:
The lab book and lecture notebook progressively introduces students to property accessors in Kotlin.
Starting: From the basics of implicit getters and setters to understanding encapsulation and the utilization of (explicit) custom-coded by us accessors for private class properties.
By building a foundation in these concepts, students can effectively manage property access, maintain data integrity, and design classes with controlled property mutation in Kotlin.

Lab 1: Understanding Implicit Getters and Setters (. dot accessor notation)
Objective: To introduce students to the concept of implicit getters and setters in Kotlin and illustrate their usage for `var` and `val` properties.
Tasks:
1. Explanation of Implicit Getters and Setters: - Introduce the concept of implicit getters and setters in Kotlin. - Explain how Kotlin automatically generates default getters and setters for properties declared with `var` and `val`. - Visualize the operation of implicit getters and setters in simplifying property access and modification.

2. Simple Example with `var` and `val` Properties:

- Lab 2: A simple example demonstrating the usage of `var` and `val` properties with implicit getters and setters. - Show how to access and modify the properties, emphasizing the implicit usage of getters and setters.

3. Exercise A:

- Ask students to create a Kotlin class with `var` and `val` properties and demonstrate property access and modification to observe the implicit usage of getters and setters.

Lab 3: Exploring Explicit Getters and Setters

Objective: To teach students how to explicitly define custom getters and setters to control the behavior of properties in Kotlin.
Tasks:
1. Introduction to Explicit Getters and Setters: - Introduce the need for explicit getters and setters to customize property access and modification behavior. - Discuss scenarios where explicit getters and setters are beneficial, such as enforcing constraints or performing additional operations.
2. **Creating Custom Getters and Setters:** - Provide examples demonstrating the creation of custom getters and setters for properties. - Illustrate how to define explicit getters and setters for `var` properties to perform specific actions when accessing or modifying the property.
3. Exercise: - Encourage students to modify the previous class created in Lab 1 to incorporate explicit custom getters and setters for one of the properties. Students should observe how the explicit getters and setters influence the behavior of the property.

Lab 4: Implementing Getters and Setters in Class Properties

Objective: To guide students in applying getters and setters for private class properties to encapsulate and control their mutation.

Tasks:

1. Understanding Encapsulation:** - Explain the concept of encapsulation and the importance of controlling access to class properties. - Emphasize the use of getters and setters to maintain encapsulation and data integrity.
2. **Using Getters and Setters for Private Properties:** - Demonstrate how to use getters and setters for private class properties to restrict direct access. - Show examples of encapsulating private properties and providing controlled access via custom getters and setters.
3. **Comprehensive Example:** - Provide a comprehensive example of a Kotlin class with private properties, along with explicit custom getters and setters, to showcase encapsulation and controlled mutation using Kotlin's property access syntax.

Lecture Notes: Kotlin Setters and Getters

I. Implicit Getters and Setters

- Kotlin automatically generates default getters and setters for `var` and `val` properties, enabling implicit property access and modification. - The default behavior of implicit getters and setters simplifies property management and usage in Kotlin.

II. Explicit Getters and Setters

- Custom getters and setters can be explicitly defined to customize the behavior of property access and modification. - Explicit getters and setters provide control over property operations, allowing the enforcement of constraints and execution of specific logic.

III. Encapsulation and Private Properties

- Getters and setters are essential for maintaining encapsulation by controlling access to private class properties.
- Private properties can be encapsulated and managed using explicit getters and setters to ensure data integrity and controlled mutation.


IV. Application in Class Design

- Custom getters and setters enable the implementation of business logic, validation, and other operations within class properties.

V. Practical Examples and Exercises

- Hands-on examples and exercises will reinforce the understanding of implicit and explicit getters and setters through practical application.

megaphone

Code Sheets

// Lab 1: Implicit Getters and Setters

// Task 1: Implicit Getters and Setters for `var` and `val` properties
class Person {
// `var` property with implicit getter and setter
var firstName: String = "John"

// `val` property with implicit getter
val lastName: String = "Doe"
}

// Task 2: Simple Example with `var` and `val` Properties
fun main() {
val person = Person()

// Implicit getter and setter for `var` property
println("Initial First Name: ${person.firstName}")
person.firstName = "Alice" // Implicit setter usage
println("Updated First Name: ${person.firstName}")

// Implicit getter for `val` property
println("Last Name: ${person.lastName}")
}


To regenerate the code so that it works with explicit accessors, modifications need to be made to the Person class allowing external access to both firstName and lastName through explicitly defined getters (and a setter for firstName, since it's a var). Given that the original properties are private, and thus not directly accessible outside the class, explicit accessor methods are required for external access. Here's how the class can be refactored:


class Person {
private var _firstName: String = "John"
private val _lastName: String = "Doe"

// Explicit getter for the `firstName` property
var firstName: String
get() = _firstName
set(value) {
_firstName = value
}

// Explicit getter for the `lastName` property
val lastName: String
get() = _lastName
}

// Task 2: Using Explicit Accessors
fun main() {
val person = Person()

// Using the explicit getter for `var` property
println("Initial First Name: ${person.firstName}")

// Using the explicit setter for `var` property
person.firstName = "Alice"
println("Updated First Name: ${person.firstName}")

Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.