Kotlin 中的接口 Interface : so much better

简介: Interface was introduced in Java as a new programming feature. It describes CAN-BE instead of IS-A relationship.

Interface was introduced in Java as a new programming feature. It describes CAN-BE instead of IS-A relationship. That also enables it to perform multiple inheritance (e.g. something can be many things, but only is a thing).

However as we know even up to Java 7 (which once was the main language for native Android Development), Interface does have various drawbacks, making it not as attractive, and at times, some have to resort back to abstract class.

With Kotlin in place, let me share with you how Kotlin made Inheritance better.

Kotlin made Interface extensible.
In Java 7, inheritance function declaration can’t have implementation. Hence those class implements an interface, need to have all it’s function implemented.

This is a problem, as this makes interface inextensible.

Imagine we have the below Movable interface.

interface Movable {
int legsCount();
}
class Horse implements Movable {
@Override
public int legsCount() {
return 4;
}
}
Then we realize that other than legs, we need to count wings too. So we add wingsCount().

It is unfortunate those that implemented this interface i.e. Horse will also need to change.

interface Movable {
int legsCount();
int wingsCount();
}
class Horse implements Movable {
@Override
public int legsCount() {
return 4;
}
@Override
public int wingsCount() {
return 0;
}
}
In Kotlin
We initially have

interface Movable {
fun legsCount(): Int
}
class Horse : Movable {
override fun legsCount() = 4
}
Then we could easily extend it.

interface Movable {
fun legsCount(): Int
fun wingsCount(): Int { return 0 }
}
class Horse : Movable {
override fun legsCount() = 4
}
Or even more, without need to modify Horse class at all!

interface Movable {
fun legsCount(): Int { return 0 }
fun wingsCount(): Int { return 0 }
fun canFly(): Boolean { return wingsCount() > 1 }
fun canWalk(): Boolean { return legsCount() > 1 }
}
class Horse : Movable {
override fun legsCount() = 4
}
Kotlin made Interface truly override.
The definition of override according to Cambridge Dictionary is

to decide against or refuse to accept a previous decision, an order, a person, etc.
In the Java world, Interface is overriding nothing.

But in Kotlin world, look at the example below

interface Movable {
fun legsCount(): Int { return 0 }
fun wingsCount(): Int { return 0 }
fun canFly(): Boolean { return wingsCount() > 1 }
fun canWalk(): Boolean { return legsCount() > 1 }
}
class Horse : Movable {
var isSick = false
override fun legsCount() = 4
override fun canWalk(): Boolean {
if (isSick) {
return false
}
return super.canWalk()
}
}
If we set horse.isSick = true, the canWalk() function will return false, regardless of the leg counts. A truly overriding capability.

Kotlin made Interface more object like
In Java world (I believe including Java 8 and 9), Interface are not allowed to have property other than final constant variable (hmm… constant variable sounds oxymoron, perhaps should be called constant value).

At most we could make an accessor function e.g. legCount().

In Kotlin
With Kotlin, one could have a property in Interface.

Instead of writing

interface Movable {
fun legsCount(): Int
fun canWalk() = legsCount() > 1
}
class Horse : Movable {
override fun legsCount() = 4
}
One could write as

interface Movable {
val legsCount : Int
fun canWalk(): Boolean = legsCount > 1
}
class Horse : Movable {
override val legsCount = 4
}
There’s some limitation for the property in Interface though, as it can’t have backfield property, which means it can’t be change. So it is still stateless.
Besides, it also can’t be initialized in the interface itself.
Kotlin made Interface a better composition
You might have heard Composition over Inheritance principle. Kotlin made this even more simpler

Imagine you have Horse and Dog. Both are 4 legs animal.

One way to program is as below

interface Movable {
val legsCount : Int
fun canWalk() = legsCount > 1
}
class Horse : Movable {
override val legsCount = 4
}
class Dog : Movable {
override val legsCount = 4
}
This is so cumbersome as we have to

replicate the code override val legsCount = 4 for each of them.

If we have more functions to override, or more class object that is 4 legs animal, we’ll have to do the same.

If one day we change to 4 to “four”, or add more functionality…

It would be a nightmare to change . So inextensible.

We can make an class inheritance of that perhaps?
interface Movable {
val legsCount: Int
fun canWalk() = legsCount > 1
}
open class FourLegged : Movable {
override val legsCount = 4
}
class Horse : FourLegged()
class Dog : FourLegged()
But this violates the Composition over Inheritance principle. Horse and Dogare not only FourLegged, but could be something else, making them very inextensible to other type anymore (e.g. Pet).

This is also inextensible ️

So let’s apply Composite over Inheritance (the traditional way)
interface Movable {
val legsCount: Int
fun canWalk() = legsCount > 1
}
object FourLegged : Movable {
override val legsCount = 4
}
class Horse : Movable {
private val movable = FourLegged
override val legsCount
get() = movable.legsCount
}
class Dog : Movable {
private val movable = FourLegged
override val legsCount
get() = movable.legsCount
}
I don’t know about you, I dislike this equally, So let’s enhance it better to as below…

interface Movable {
val legsCount: Int
fun canWalk() = legsCount > 1
}
object FourLegged : Movable {
override val legsCount = 4
}
open class MovableImpl(private val movable: Movable) : Movable {
override val legsCount
get() = movable.legsCount
}
class Horse : MovableImpl(FourLegged)
class Dog : MovableImpl(FourLegged)
Now this is better, as it is more extensible, as in the future we have FourLegged or TwoLegged etc, we could easily add to it.

But I still dislike it, as I need to have the intermediate class MovableImpl. So let’s check out further what how Kotlin could made our interface better…

The Kotlin provided way: By … delegate to composition made easy
With the interface in Kotlin, we could use the By keyword to generate the Delegate pattern so easily. Check it out

interface Movable {
val legsCount: Int
fun canWalk() = legsCount > 1
}
object FourLegged : Movable {
override val legsCount = 4
}
class Horse : Movable by FourLegged
class Dog : Movable by FourLegged
So much nicer! . Hopes you see how good that is.

Kotlin 开发者社区

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

img_f0a9a5e3c63edb2cda8899c204e13bbf.jpe
开发者社区 QRCode.jpg
相关文章
|
8月前
|
Kotlin
Kotlin中接口、抽象类、泛型、out(协变)、in(逆变)、reified关键字的详解
Kotlin中接口、抽象类、泛型、out(协变)、in(逆变)、reified关键字的详解
60 0
|
Java 开发工具 Spring
springBoot搭建自己的第一个接口(kotlin+gradle)
springBoot搭建自己的第一个接口(kotlin+gradle)
207 0
springBoot搭建自己的第一个接口(kotlin+gradle)
|
安全 JavaScript 前端开发
Kotlin 之类和接口
Kotlin 之类和接口
Kotlin 之类和接口
|
Java 编译器 Kotlin
Kotlin学历之抽象类与接口
Kotlin学历之抽象类与接口
83 0
Kotlin学历之抽象类与接口
|
设计模式 安全 Java
带你踏入Kotlin大门(六)|基本功_接口和特殊类篇
这篇文章是我们 Kotlin 基础章系列的最后一文了,前面我们利用了5篇文章讲解了 Java 开发者如何学好 Kotlin 的基本用法,每篇文章的篇幅都不长,可以在空余时间快速阅读,笔者希望学习完前五篇,再来学习这最后一篇文章,会更加的容易理解。
|
Java Kotlin
Kotlin 之 匿名接口 回调
Kotlin 之 匿名接口 回调
301 0
|
IDE Java 编译器
Kotlin 1.5 新特性:密封接口比密封类强在哪?
Kotlin 1.5 推出了密封接口(Sealed Interface),这与密封类(Sealed Class)有什么区别呢?
343 0
Kotlin 1.5 新特性:密封接口比密封类强在哪?
|
Java Kotlin
【Kotlin 初学者】抽象类-嵌套类-单例类-接口
目录 一、抽象类 二、嵌套类(内部类) 三、单例类 3.1 object 关键字 3.2 对象声明 3.3 对象表达式 3.4 伴生对象 四、接口 4.1 接口定义 4.2 默认实现 4.3 小结
164 0
【Kotlin 初学者】抽象类-嵌套类-单例类-接口
|
Java Android开发 Kotlin
kotlin学习笔记——类、函数、接口
1、类 类是空的可以省略大括号, 如: class A(name: String) 注意:kotlin与java的一个不同点,代码句结尾不必加“;”号
246 0
|
Kotlin
【Kotlin】接口 ( 声明 | 实现 | 接口方法 | 接口属性 | 接口覆盖冲突 | 接口继承 )(二)
【Kotlin】接口 ( 声明 | 实现 | 接口方法 | 接口属性 | 接口覆盖冲突 | 接口继承 )(二)
238 0