I keep digging into Liskov's principle and although I have managed to understand it, I have a doubt... the definition tells us that to comply with Liskov's principle, the parent class must be able to be replaced by the child classes without having to alter the code and being able to use all its functions.
Then the following question has arisen in my mind. Let's imagine we have the typical duck casuistry:
open class Duck {
open fun swim() {
println("The duck is swimming")
}
open fun quack() {
println("The duck says quack")
}
open fun walk() {
println("The duck is walking")
}
}
class NormalDuck: Duck()
class MetalDuck: Duck() {
override fun swim() {
throw NotImplementedException("MetalDucks can't swim")
}
}
Applying Liskov's principle I would be left with something like:
open class Duck {
open fun quack() {
println("The duck says quack")
}
open fun walk() {
println("The duck is walking")
}
}
open class DuckThatCanSwim : Duck() {
open fun swim() {
println("The duck is swimming")
}
}
class NormalDuck : DuckThatCanSwim()
class MetalDuck : Duck()
val duck = Duck()
duck.quack()
duck.walk()
In this case there is no problem, I can comply with the definition of the principle since I have a Kotlin open class and I can create an instance of it and even if I change it to NormalDUck or MetalDuck, its parent class methods will still work.
But I've seen people posting examples with interfaces, something like:
interface Duck {
fun quack()
fun walk()
}
interface DuckThatCanSwim : Duck {
fun swim()
}
class NormalDuck : DuckThatCanSwim {
override fun swim() {
println("The duck is swimming")
}
override fun quack() {
println("The duck says quack")
}
override fun walk() {
println("The duck is walking")
}
}
class MetalDuck : Duck {
override fun quack() {
println("The duck says quack")
}
override fun walk() {
println("The duck is walking")
}
}
In this case, although Liskov's principle is also fulfilled, it is not completely fulfilled, since I cannot create an instance of an interface, so... with Liskov is it correct to use interfaces or should only classes be used in order to fulfill the whole statement?