在 Java 中,接口可以看成是抽象类的更进一步抽象,在 Kotlin 中也是这样的。 在 Kotlin 中,接口与抽象类的不同表现在:
- 用 interface 声明,抽象方法可以省略 abstract 关键字;
- 成员变量必须是 abstract 的,否则需要提供 get() 方法的实现;
根据上面的描述,我们得知 Kotlin 接口是可以含有普通方法的,这一点是跟 Java 不同的:
interface IFly {
fun fly()
fun accelerate() {}
}
在 Kotlin 中,我们不用在像 Java 那样,继承基类使用 extends 关键字、实现接口使用 implements 关键字,徒增记忆负担。在 Kotlin 中我们实现接口的方式也与抽象类是一致的,只需要一个冒号就行了:
class Bird : IFly {
override fun fly() {}
}
属性
说到属性,这是跟 Java 差别较大的部分。Kotlin 的属性必须是 abstract 的,否则必须提供获取该属性的 get() 方法。这是因为接口中的属性没有影子字段(backing field),无法使用默认的 get() 方法来引用他们。
interface IFly {
val speed: Int
val wings: String
get() = "wings"
fun fly()
}
class Bird : IFly {
override val speed: Int = 30
override fun fly() {
TODO("Not yet implemented")
}
}
继承其他接口
与 Java 一样,Kotlin 中的接口也可以继承其他的接口:
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String
get() = "$firstName $lastName"
}
data class Employee(
override val firstName: String,
override val lastName: String,
val position: Int
) : Person
方法冲突
如果一个类实现了多个接口,多个接口里面有完全相同的方法签名,这时会发生什么?
interface A {
fun foo() {
println("A")
}
fun bar()
}
interface B {
fun foo() {
println("B")
}
fun bar() {
println("bar")
}
}
class C : A {
override fun bar() {
println("bar")
}
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super.bar()
}
}
接口 A 和 B 里面都有 foo() 和 bar(),二者都实现了 foo(),只有 B 实现了 bar()。 对于 C,很简单,按照规则实现接口的抽象方法 bar() 即可。 比较特殊的是 D,D 同时实现了 A 和 B 两个接口,而且二者中含有相同的方法签名 foo() 和 bar()。这时我们需要把 foo() 和 bar() 都实现才行。
在 Java 中是怎么样的? 首先,Java 中的接口是不能含有已经实现的的方法的。所以 Java 中不存在 Kotlin 中必须要实现接口中已经实现了 foo() 方法的现象。 如果多个接口中含有相同的方法签名,是都要实现的:
public interface A {
void foo();
void bar();
}
public interface B {
void foo();
void bar();
}
public class D implements A, B {
@Override
public void foo() {}
@Override
public void bar() {}
}
方法型接口
从 1.4 版本开始,如果一个接口只含有一个抽象方法,我们称这样的接口为方法型接口,或者孤独抽象方法(SAM,single abstract method)。方法型接口里面还可以有多个非抽象方法。 在 Kotlin 中我们使用 fun 修饰符来声明方法型接口:
fun interface KRunnable {
fun invoke()
}
SAM 转换
SAM 的好处是,我们可以使用 lambda 表达式来让代码更加简洁和具有更好的可读性。 通常当我们使用接口时,我们需要为接口创建一个对象再使用。而当我们使用 SAM 时,我们可以直接使用 lamdbda 表达式,然后编译器会自动帮我们做转换,把 lambda 表达式转换成对应的对象。 我们有一个 SAM:
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
支持情况下我们的使用方法是:
val isEven = object : IntPredicate {
override fun accept(i: Int): Boolean {
return i % 2 == 0
}
}
借助 SAM 转换,我们可以使用 lambda 表达式来完成:
val isEven = IntPredicate { i -> i % 2 == 0 }
对于我们在安卓开发中经常使用的接口,比如 Runnable、OnClickListener 等只含有一个抽象方法的接口,都是可以使用 SAM 转换的:
mButton.setOnClickListener {
}
|