前言:不要当父母需要你的时候,除了泪水,一无所有;不要当孩子需要你的时候,除了惭愧一无所有;不要当自己回首过去的时候,除了蹉跎一无所有。一、概述 ??Kotlin 中的接口可以包含抽象方法的声明,也可以包含方法实现。接口与抽象类的区别在于接口不能存储状态。它们可以具有属性,但是这些属性必须是抽象的或者提供访问器实现。同时在 Kotlin1.4版本开始支持接口的SAM转换了。
Kotlin 中接口与 Java8 类似,使用
interface
关键字定义接口,允许方法有默认实现:interface MyInterface {
fun share() //未实现,默认 abstract 修饰
fun invite() {//已实现,不是 abstract 修饰
println("invite()")//可选方法体
}
}
接口中未提供实现的方法默认是
abstract
修饰的,提供实现的则不是 abstract
修饰的。子类必须重写抽象成员。二、接口的使用 2.1 实现接口
一个类或者对象可以实现一个或者多个接口。Kotlin 中没有像 Java 那样提供
implements
关键字实现,而是通过冒号:
来表示,后面接需要实现的接口名称。class Student : MyInterface {
override fun share() {//该方法是 abstract 修饰的,所以子类必须实现,而 invite() 有默认实现,不是必须重写的
//TODO
println("share()")
}
} //调用
var student = Student()
student.share()
student.invite()
打印数据如下:
share()
invite()
abstract
修饰的方法在子类中是必须要重写的,当然没有abstract
修饰的方法你也可以重写提供自己的实现。2.2 接口属性
你可以在接口中声明属性,在接口中声明的属性可以是抽象的,也可以为访问器提供实现。接口中声明的属性不能有后备字段
field
,所以接口中声明的访问器不能引用后备字段。interface MyInterface {
abstract val name: String //默认 abstract 修饰
val type: Int
//提供 get 实现,但是不能有后备字段 field,不是 abstract 修饰
get() = 10//var age: Int//报错,声明的属性不能有后备字段 field
//get() = field//var sex: String = "女性"//接口中的属性不允许被初始化
}class Student : MyInterface {
override val name: String //abstract修饰的属性必须重写
get() = "Android"
}
接口中的属性默认是抽象的,不允许被初始化值,接口不会保存属性值,实现接口时必须重写属性。
2.3 接口继承
Kotlin 中的接口是可以继承的,这点和 Java 类似。既可以为成员提供实现,又可以声明新的成员(函数和属性)。所以,实现这种接口的类只需要重写尚未实现的成员即可。
//基类接口
interface BaseInterface {
val name: String //声明一个属性,默认抽象fun send1() //声明一个方法,默认抽象fun send2() {//声明一个方法,提供默认实现
println("send2()")
}
}//子类接口:继承 BaseInterface,实现了属性 name
interface ChildInterface : BaseInterface {
val childName: String //声明一个属性,默认抽象override val name: String //实现 BaseInterface 接口中的 name 属性
get() = "Android"
}//具体实现类:实现了 ChildInterface 接口
class People : ChildInterface {
override val childName: String //接口 ChildInterface 中尚未实现的
get() = "Kotlin"override fun send1() {//接口 BaseInterface 中尚未实现的
println("send1()")
}
}
可以看到,类 People 中必须实现了
childName
,而没有实现 name
,为什么?因为 ChildInterface 接口已经重写了
name
,所以属性 name
已经不是抽象的了,那么类 People中就不必要实现了;而 childName
在 ChildInterface 中没有提供实现,仍是抽象的,所以在 People 中必须重写,并提供实现。那么为什么类 People 中必须实现了
send1()
,而没有实现 send2()
?【Kotlin专题「十」(接口与函数接口(Functional (SAM) interfaces))】同理,因为
send1()
在 BaseInterface 中没有提供实现,在ChildInterface 中也没有提供实现,是抽象的,所有在 People 中必须重写,并提供实现。而 send2()
在 BaseInterface 中提供了实现,是不是抽象的,所有不是必须重写的。为什么 ChildInterface 可以不实现
send1()
而类 People 中必须实现?因为 ChildInterface 本身是个接口,可以提供
abstract
成员, send1()
是抽象的,而 People 是个具体实现类,它必须实现父接口中未实现的方法和属性,除非 People 也是个抽象类。总的来说:实现类必须重写尚未实现的函数和属性。
2.4 接口重写冲突
所谓的接口重写冲突和前面讲解的类继承方法冲突是相似的。指在一个子类中,实现了多个父接口,而不同的父接口有相同的方法名称,这就会给子类实现时造成冲突,无法确认调用的是哪个父类的实现。
interface A {
fun foo() {//已有默认实现,不会强制子类实现
println("A:foo()")
}fun bar()//没有默认实现,会强制子类实现
}interface B {
fun foo() {//已有默认实现,不会强制子类实现
println("B:foo()")
}fun bar() {//已有默认实现,不会强制子类实现
println("B:bar()")
}
}//实现类,实现A接口
class C : A {
override fun bar() {//A 中 bar() 没有实现,仍是抽象的,需要重写,并提供实现
println("C:bar()")
}
}//实现类,同时实现 A, B 两个接口
class D : A, B {
//强制重写 bar(),因为 A 中的 bar() 没有实现
override fun bar() {
super.bar() //实际上调用的是 B 中的 bar(),因为 A 中 bar()没有实现,仍是抽象的,不能被访问
}//虽然接口 A 和 B 中的 foo() 方法都提供了默认实现,但是名字相同给子类 D 带来了冲突,无法确认调用的是 A 中的 foo() 实现还是 B 中的 foo() 实现,所以 Kotlin 强制子类重写
override fun foo() {
super.foo()//调用 A 中的 foo()
super.foo()//调用 B 中的 foo()
}
}
接口A,B都声明了函数
foo()
和 bar()
,它们都默认实现了 foo()
,但是只有接口B中的 bar()
提供了默认实现,接口A中的 bar()
没有提供默认实现,默认为抽象。如果我们在接口A派生一个具体实现类C,那么必须重写 bar()
并提供一个实现。如上面的 class C。但是,如果从接口A和B派生一个具体实现类D,需要实现多个接口继承的所有方法,并指定类D应该如何确切地实现它们。当如果有多个相同方法,则必须重写该方法,使用
super<父类名>
选择性调用父类的实现。如上面的 class D。虽然接口A和B中的
foo()
方法都提供了默认实现,但是名字相同给子类D带来了冲突,无法确认调用的是A中的 foo()
实现还是B中的 foo()
实现,所以Kotlin强制子类重写 foo()
。因为A中的 bar()
没有提供默认实现,也需要强制重写。 //调用
val d = D()
d.foo()
d.bar()val c = C()
c.foo()
打印数据如下:
A:foo()
B:foo()
B:bar()
C:bar()
三、函数接口 ??只有一个抽象方法的接口称为函数接口或者称为单个抽象方法(SAM)接口。函数接口可以有几个非抽象成员,但是只能由一个抽象成员。
要在 Kotlin 中声明一个函数接口,使用
fun
修饰符修饰接口://函数接口使用fun关键字修饰接口
fun interface KRunnable {
fun invoke()
}
3.1 SAM转换
在 Kotlin1.4版本开始,对于函数接口,您可以使用SAM转换,通过使用 Lambda 表达式,使代码更简洁可读。
有人会问什么是‘SAM转换’?
SAM 转换,即 Single Abstract Method Conversions,就是对于只有单个非默认抽象方法接口的转换,对于符合条件的接口称为
SAM Type
,在 Kotlin 中可以直接使用 Lambda 表达式来表示,前提是 Lambda 所表示的函数类型能够与接口中的方法相匹配。你可以使用 Lambda 表达式,而不是手动创建实现函数接口的类。通过SAM转换,Kotlin 可以将其签名与接口的单一方法的签名相匹配的任何 Lambda 表达式转换为实现该接口的类的实例。
举个例子,下面这个函数接口:
fun interface IntAction {
fun check(int: Int): Boolean
}
SAM转换前这个接口的实现方式:
//创建一个实现接口的类实例
var isEvent = object : IntAction {
override fun check(int: Int): Boolean {
return int % 2 == 0
}
}
通过利用 Kotlin 的SAM转换,你可以编写以下等价代码:
var isEvent = IntAction { it % 2 == 0 }
一个简单的 Lambda 表达式简化了很多不必要的代码:
//函数接口
fun interface IntAction {
fun check(int: Int): Boolean
}
//Lambda 表达式的接口实现类
var isEvent2 = IntAction { it % 2 == 0 } fun main() {
println("8 是否能被 2 整除:${isEvent.check(8)}")
}
打印数据如下:
8 是否能被 2 整除:true
当然,你也可以对 Java 接口使用SAM转换。
3.2 函数接口和类型别名
函数接口和类型别名用于不同的目的。类型别名只是现有类型的名称——它们不会创建新类型,而函数接口可以。
类型别名只能有一个成员,而函数接口可以有多个非抽象成员和一个抽象成员。函数接口还可以实现和扩展其他接口。
综上所述,函数接口比类型别名更灵活,提供的功能也更多。
至此,本文结束!
源码地址:https://github.com/FollowExcellence/KotlinDemo-master
请尊重原创者版权,转载请标明出处:https://blog.csdn.net/m0_37796683/article/details/107964620 谢谢!
推荐阅读
- 快速上手 Kotlin 开发系列之函数与函数嵌套
- 加深学习|android属性动画(Kotlin)
- android|一个简单的Android圆形ProgressBar
- Kotlin专题「十一」(可见性修饰符(private、protected、internal、public))
- Kotlin专题「十三」(数据类(Data Classes))
- Kotlin专题「十四」(密封类(Sealed Classes))
- Kotlin专题「十二」(扩展Extensions(扩展函数与属性))
- Android开发者快速上手Kotlin(三) 之 高阶函数和SAM转换
- kotlin数字与java数字的不同