ref – https://medium.com/@eneszor95/big-challenge-in-kotlin-interface-vs-abstract-class-520cc234e7c1
Abstract let’s other classes inherit from the abstract class.
Interface, let’s others conform to it.
Interfaces can have abstract methods
We provide a base abstract function. If you over-ride it in your class, it will call it. Otherwise, it will default down to the base abstract function in your interface.
In our example, addMilk is over-ridden. But we did not implement addCoffee, so it calls CoffeeMachineInterface’s addCoffee.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
interface CoffeeMachineInterface { // abstract method addMilk fun addMilk(){ println("CoffeeMachineInterface - Milk is adding...") } // abstract method addCoffee fun addCoffee(){ println("CoffeeMachineInterface - Coffee is being added...") } } class SuporCoffee : CoffeeMachineInterface { // we over-ride abstract method addMilk override fun addMilk() { println("SuporCoffee - Adds Foamy Milk") } // we didn't over-ride abstract method addCoffee, so it would execute the interface's abstract method } fun main(args: Array<String>) { println("Hello World!") val cup1 = SuporCoffee() cup1.addMilk() cup1.addCoffee() } |
output:
SuporCoffee – Adds Foamy Milk
CoffeeMachineInterface – Coffee is being added…
Interfaces can be properties…but need to be abstract
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
interface CoffeeMachineInterface { // you cannot initialize it. classes that conform to this interface must initialize it var coffeeAmount: Int } class SuporCoffee : CoffeeMachineInterface { override var coffeeAmount: Int = 0 get() { println("get() on 'coffeeAmount' for SuporCoffee") return field; } set(value) { println("set() on 'coffeeAmount' for SuporCoffee") field = value } } fun main(args: Array<String>) { println("Hello World!") val cup1 = SuporCoffee() cup1.coffeeAmount = 16 println("cup1 has ${cup1.coffeeAmount} oz of coffee") } |
output:
set() on ‘coffeeAmount’ for SuporCoffee
get() on ‘coffeeAmount’ for SuporCoffee
cup1 has 16 oz of coffee
An interface can be inherited from other interface.
abstract classes cannot be instantiated. You must derive from it, and then instantiate it.
1 2 3 4 5 6 7 8 9 10 |
abstract class Person { var age: Int = 40 fun displaySSN(ssn: Int) { println("My SSN is $ssn.") } abstract fun displayJob(description: String) } |
- an abstract class Person is created. You cannot create objects of the class.
- the class has a non-abstract property age and a non-abstract method displaySSN. IF you need to override these members in the subclass, they should be marked with open keyword.
- The class has an abstract method displayJob(). It doesn’t have any implementation and MUST be overridden in its subclasses.
So we create class Teacher and override displayJob. Then when we instantiate it, we can access its own implementation of displayJob and the abstract’s final base function displaySSN.
1 2 3 4 5 6 7 8 9 10 11 12 |
class Teacher(name: String): Person(name) { override fun displayJob(description: String) { println(description) } } fun main(args: Array<String>) { val jack = Teacher("Jack Smith") jack.displayJob("I'm a mathematics teacher.") jack.displaySSN(23123) } |
Also, the practical side of abstract classes is that you can encapsulate a part of implementation that works with the state, so that it cannot be overridden in the derived classes.
Abstract properties can be initialized or abstract. When it is abstract, it cannot be initialized. It must be over-riden in the derived class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
abstract class CoffeeMachineController { // Property with initializer cannot be abstract. Thus, we declare abstract, it must NOT be initialized. // private as default abstract var coffeeAmount: Int? } class SuporCoffee: CoffeeMachineController() { override var coffeeAmount: Int? = 8 } fun main(args: Array<String>) { val cup1 = SuporCoffee() println("cup1 has ${cup1.coffeeAmount} oz of coffee"); } |
Property that are initialized must have keyword ‘open’
Abstract can hold state by using open and initializing a property. We then have the option of over-riding it in a derived class.
1 2 3 4 5 6 7 8 |
abstract class CoffeeMachineController { // Abstract can hold state like so. But we need use open. open means, it is override-able open val milkAmount: Int? = 0 } class SuporCoffee: CoffeeMachineController() { override val milkAmount: Int? = 8 } |