Kotlin Open & Abstract Classes and How Inheritance Works
In object-oriented programming, inheritance enables new objects to take on the properties of existing objects. We can have class that can inherit properties and methods from the parent class, when we inherit classes, we do not need to specify the method and properties again, in the new class (child class or sub class). In essence it allow us to avoid code duplication (important) and make our code more flexible and maintainable (also very important).
By default, classes in kotlin are final i.e they cannot be inherited from. This makes sense because not all classes created will need to be inherited or opened
What do student and employee have in common?
[box type="info" align="" class="" width=""]When coding always follow the DRY (Don't Repeat Yourself) principle. A rule of thumb is to create a class to define the common attributes and functions of the Objects (in this case Student and Employee) your trying to model and then inherit from it[/box]
Differences between a Student and an Employee
Instead of writing our code like this :
we write like this:
we overrode the variables name and age (in the constructor), so that the name and age in the constructor of super class (Person) can be unified. such when we change age for example it will reflect in both the child class and parent class. The code below demonstrate the concept succinctly.
the reason we might want to override a method ( using the open keyword) is that there may be some similarities between the functions of a student and an Employee, for example a student can change college level while an employee can get promoted, these are similarities but they are manifested a little differently. An Employee might get a double promotion while a student must go through a level at a time. For that reason we can simply create a function incrementLevel and override it in our child class and define the behavior we want for the object.
Methods declared open have a default implementation (i.e they have a method body), which would be used if the method was not overridden. In essence overriding a method declared open is optional.
1 - Abstract methods on the other hand do not have a method body at all.
2 - Abstract methods demand that their classes be declared abstract.
3 - Classes declared 'abstract' can be inherited but must implement abstract method in sub class
4 - Abstract methods must be overridden in any class that inherits its class.
An example of abstract method is a method called study. A student needs to study to pass an exam while an Employee needs to study where he/she works, to improve it's efficiency, productivity and bring revenue. Even though they are both named study they perform wildly different implementations. For this we can use abstract methods. Since study is crucial to the success of both of them (they both must study or get FIRED! ) in their fields, abstract method is the way to go.
By default, classes in kotlin are final i.e they cannot be inherited from. This makes sense because not all classes created will need to be inherited or opened
Why do we Inherit classes?
Let's say, we wanted to create a class for Student and Employee.What do student and employee have in common?
- they both need to eat
- they both sleep
- they both move
- they both need to bath
- they both have a name
- the both have hair (no matter how small)
- they both breath air
- they both have birthdays
- they both ... You get the idea?
[box type="info" align="" class="" width=""]When coding always follow the DRY (Don't Repeat Yourself) principle. A rule of thumb is to create a class to define the common attributes and functions of the Objects (in this case Student and Employee) your trying to model and then inherit from it[/box]
Differences between a Student and an Employee
- An Employee gets paid, a student is not
- An Employee doesn't need to study for exam, a student has to
- An Employee can buy Expensive things, a student, probably not
- A student has a Matriculation or ID number, an Employee? probably not
- ....
Instead of writing our code like this :
class Example{ fun main (args: Array<String { val employee = Employee("Joe", 32) println(employee.yearOfBirth()) val student = Student("Jack", 22) println(student.yearOfBirth()) } } //a lot of duplicate code class Student(val name: String, var age: Int) { fun move() { println("$name moved!") } fun greet(name : String){ println("Good Morning $name) } fun yearOfBirth() = Calendar.getInstance().get(Calendar.YEAR) - age fun birthday() { age++ } } class Employee(val name: String, var age: Int) { fun move() { println("$name moved!") } fun greet(name : String){ println("Good Morning $name) } fun yearOfBirth() = Calendar.getInstance().get(Calendar.YEAR) - age fun birthday() { age++ } }[box type="warning" align="" class="" width=""]the above code is hard to maintain and can not scale because there are too many duplicate codes which perform the exact same function. If we need to modify something, we would need to modify it in both classes. the situation gets worse if we have many functions and variables[/box]
we write like this:
open class Person(open val name: String, open var age: Int){ // open declares our class as a class we can inherit from fun move() { println("$name moved!") } fun greet(name : String){ println("Good Morning $name") } fun yearOfBirth() = Calendar.getInstance().get(Calendar.YEAR) - age fun birthday() { age++ } } class Employee(override val name: String, override var age: Int) : Person(name, age){ // inheriting is done after the colon // we also have to call the constructor of the parent class } class Student(override val name: String, override var age: Int) : Person(name, age){ // inheriting is done after the colon } class Example{ fun main (args: Array<String>){ val employee = Employee("Joe", 32) println(employee.yearOfBirth()) val student = Student("Jack", 22) println(student.yearOfBirth()) } }As you can see from above we've eliminated a lot of spaghetti (duplicate) code and saved our self heart ache of maintainability and scaling.
we overrode the variables name and age (in the constructor), so that the name and age in the constructor of super class (Person) can be unified. such when we change age for example it will reflect in both the child class and parent class. The code below demonstrate the concept succinctly.
class Example{ fun main(args: Array<String>){ val student = StudentEx("Carter", 17) student.print() // Name = Carter, Age = 17 student.studentAge = 23 // age changed in the StudentEx class but print() function is in PersonEx class student.print()// Name = Carter, Age = 17 student.printStudentAge() // 'Student Age = 23' student.age = 23 //age changed in the PersonEx class and print() function is in PersonEx class student.print() // Name = Carter, Age = 23 val employee = EmployeeEx("Anthony", 28) employee.print() // Name = Anthony, Age = 28 employee.employeeAge = 31 // age changed in the EmployeeEx class but print() function is in Person class employee.print() // 'Name = Anthony, Age = 28' values remain unchanged employee.printEmployeeAge() // 'Employee Age = 31' employee.age = 31 //age changed in the PersonEx class and print() function is in PersonEx class employee.print() // 'Name = Anthony, Age = 31' employee.printEmployeeAge() } } class StudentEx(val studentName :String, var studentAge: Int): PersonEx(studentName, studentAge){ fun printStudentAge(){ println("Student Age = $studentAge") } } class EmployeeEx(val employeeName :String, var employeeAge: Int): PersonEx(employeeName, employeeAge){ fun printEmployeeAge(){ println("Employee Age = $employeeAge") } } open class PersonEx (open val name: String, open var age:Int){ fun print() { println("Name = $name, Age = $age") } } // Utilizing open and override keywords eliminates boiler plate code class Example{ fun main(args: Array){ val student = StudentEx("Carter", 17) student.print() // Name = Carter, Age = 17 student.age = 23 // age changed in both StudentEx and Person class student.print()// Name = Carter, Age = 23 student.printStudentAge() // 'Student Age = 23' student.age = 24 //age changed in both StudentEx and Person class student.print() // Name = Carter, Age = 24 val employee = EmployeeEx("Anthony", 28) employee.print() // Name = Anthony, Age = 28 employee.age = 31 // age changed in both EmployeeEx and Person class employee.print() // 'Name = Anthony, Age = 31' employee.printEmployeeAge() // 'Employee Age = 31' employee.age = 32 // age changed in both EmployeeEx and Person class employee.print() // 'Name = Anthony, Age = 32' employee.printEmployeeAge() } } class StudentEx(override val name :String, override var age: Int): PersonEx(name, age){ fun printStudentAge(){ println("Student Age = $age") } } class EmployeeEx(override val name :String, override var age: Int): PersonEx(name, age){ fun printEmployeeAge(){ println("Employee Age = $age") } } open class PersonEx (open val name: String, open var age:Int){ fun print() { println("Name = $name, Age = $age") } }Keywords:
- 'open' Keyword is used on either variable or class we want to inherit
- 'override' let us override properties in our parent class
- In order to inherit a class, you must declare the class as
open
orabstract
- each property we may want to override should be declared
open
orabstract
- condition 2 goes for methods too.
class InheritanceExample { fun main(args: Array<String>) { val studentObject = Student("Romeo", 20, 167289) val employeeObject = Employee("Joe", 31, "Quality Assurance Officer") employeeObject.move() // method in parent class studentObject.move() // method in parent class studentObject.writeExam() // method in child class employeeObject.getPaid() // method in child class } } class Student(override val name: String, override var age: Int, val matricNo: Long) : Person(name, age) { // A student has an identification number fun writeExam() { // A student has to write exams println("Boy, this exam is simple") } } class Employee(override val name: String, override var age: Int, var jobTitle: String) : Person(name, age) { // A job usually has a title fun getPaid() { // An employee get's paid println("Yay it's pay day") } } open class Person(open val name: String, open var age: Int) { fun move() { println("$name just moved!") } fun eat() { println("$name is eating!") } fun sleep() { println("$name is sleeping!") } fun yearOfBirth() = Calendar.getInstance().get(Calendar.YEAR) - age fun birthday() { age++ } }open keyword with Methods
the reason we might want to override a method ( using the open keyword) is that there may be some similarities between the functions of a student and an Employee, for example a student can change college level while an employee can get promoted, these are similarities but they are manifested a little differently. An Employee might get a double promotion while a student must go through a level at a time. For that reason we can simply create a function incrementLevel and override it in our child class and define the behavior we want for the object.
Methods declared open have a default implementation (i.e they have a method body), which would be used if the method was not overridden. In essence overriding a method declared open is optional.
class InheritanceExample { fun main(args: Array<String>) { val studentObject = Student("Romeo", 20, 167289, 1) studentObject.incrementLevel() // Yay! Student Romeo with ID 167289 is now in Year 2 val employeeObject = Employee("Joe", 31, "Quality Assurance Officer", 5) employeeObject.incrementLevel() // Romeo Level has changed } } class Student(override val name: String, override var age: Int, val matricNo: Long, var academicYear: Int) : Person(name, age) { fun writeExam() { println("Boy, this exam is Simple!") } override fun incrementLevel() { println("Yay! Student $name with ID $matricNo is now in Year ${academicYear++} ") } } class Employee(override val name: String, override var age: Int, var jobTitle: String, var jobLevel: Int) : Person(name, age) { fun getPaid() { println("Yay it's pay day") } override fun incrementLevel() { println("Yay! $name has been promoted to Level ${jobLevel++} ") } } open class Person(open val name: String, open var age: Int) { fun move() { println("$name moved!") } fun yearOfBirth() = Calendar.getInstance().get(Calendar.YEAR) - age open fun incrementLevel() { println("$name Level has changed") // This will be executed if method is not overrode } }Abstract Methods
1 - Abstract methods on the other hand do not have a method body at all.
2 - Abstract methods demand that their classes be declared abstract.
3 - Classes declared 'abstract' can be inherited but must implement abstract method in sub class
4 - Abstract methods must be overridden in any class that inherits its class.
An example of abstract method is a method called study. A student needs to study to pass an exam while an Employee needs to study where he/she works, to improve it's efficiency, productivity and bring revenue. Even though they are both named study they perform wildly different implementations. For this we can use abstract methods. Since study is crucial to the success of both of them (they both must study or get FIRED! ) in their fields, abstract method is the way to go.
class InheritanceExample { fun main(args: Array<String>) { val studentObject = Student("Romeo", 20, 167289, 1) studentObject.study() // This's where we Implement what Study means to a Student val employeeObject = Employee("Joe", 31, "Quality Assurance Officer", 5) employeeObject.study() // Here's where we Implement what Study means to an Employee } } class Student(override val name: String, override var age: Int, val matricNo: Long, var academicYear: Int) : Person(name, age) { override fun study() { println("This's where we Implement what Study means to a Student") } } class Employee(override val name: String, override var age: Int, var jobTitle: String, var jobLevel: Int) : Person(name, age) { override fun study() { println("Here's where we Implement what Study means to an Employee") } } abstract class Person(open val name: String, open var age: Int) { fun move() { println("$name moved!") } open fun incrementLevel() { println("$name Level has changed") } abstract fun study() // abstract method, no method body }
Conclusion
putting it all together :class InheritanceExample { fun main(args: Array<String>) { //instatiating object val studentObject = Student("Romeo", 20, 167289, 1) val employeeObject = Employee("Joe", 31, "Quality Assurance Officer", 5) studentObject.study() // This's where we Implement what Study means to a Student studentObject.incrementLevel() // prints 'Yay! Student Joe with ID 167289 is now in Year 1' from Student class studentObject.writeExam() // method in child class employeeObject.study() // Here's where we Implement what Study means to an Employee employeeObject.getPaid() // method in child class employeeObject.incrementLevel() // prints 'Joe level has changed' from the parent class of Employee } } class Student(override val name: String, override var age: Int, val matricNo: Long, var academicYear: Int) : Person(name, age) { fun writeExam() { // A student has to write exams println("Boy, this exam is simple") } override fun incrementLevel() { println("Yay! Student $name with ID $matricNo is now in Year ${academicYear++}") } override fun study() { println("This's where we Implement what Study means to a Student") } } class Employee(override val name: String, override var age: Int, var jobTitle: String, var jobLevel: Int) : Person(name, age) { override fun study() { println("Here's where we Implement what Study means to an Employee") } fun getPaid() { println("Yay it's pay day") } } abstract class Person(open val name: String, open var age: Int) { fun move() { println("$name just moved!") } fun eat() { println("$name is eating!") } fun sleep() { println("$name is sleeping!") } fun yearOfBirth() = Calendar.getInstance().get(Calendar.YEAR) - age fun birthday() = age++ open fun incrementLevel() { println("$name Level has changed") } // abstract method, no method body abstract fun study() }[box type="note" align="" class="" width=""]Hey there! Thanks for reading through! Please use the comment section below to post your suggestions, comments or reviews. You noticed an error? please let me know! am in the process of becoming a better writer and developer. Thanks, Love You![/box]
Comments
Post a Comment