继承
介绍:继承主要就是为了重构或者实现父类定义的抽象方法。那么能从父类继承的模块主要分为:构造器,函数,属性。
Kotlin之中所有的类都继承Any类它是所有类的超类。可以参照java之中的所有类都是继承Object类来进行理解。
Any 类默认提供了三个函数:
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
这三个函数很好理解。java之中也一样都有这三个函数。
实例:
//定义一个基类, 构造传参name
open class Person(var name: String) {
}
//继承Person 类,实现了初始化构造
class Boy(name: String) : Person(name) {
//重构了toString方法
override fun toString(): String {
return name
}
}
fun main(array: Array<String>) {
var boy = Boy("Z同学")
println(boy.toString())
}
输出:
Z同学
重构构造器
如果子类有主构造器, 则基类必须在主构造器中立即初始化。
实例1:
//定义一个基类, 构造传参name
open class Person(var name: String) {
}
class Girl(name:String,age:Int):Person(name){
//子构造器配置参考
constructor(name:String,address:String,age:Int):this(name,age){
}
}
如果子类没有构造器,但是父类有主构造器。则必须在每一个二级构造器之中用super关键字初始话父类。和java之中继承的时候需要采用super初始话父类的构造函数一样的要求。
实例2:
class Baby : Person {
constructor(name: String) : super(name) {
}
constructor(name: String, six: Int) : this(name) {
}
}
重构函数
介绍:在父类中使用fun
声明的函数默认修饰final
。代表该函数不允许被重构。
子类只能使用该函数不能重构。只有父类标注了'open' 关键字的函数才能被重构。
在子类之中标注了'override' 代表该函数重构于父类的函数
//定义一个基类, 构造传参name
open class Person(var name: String) {
//可以继承,但是不能重构
fun name(): String {
return name
}
open fun study() {
println("")
}
}
class Student(name: String) : Person(name) {
override fun study() {
println("我还在读书,没有毕业")
}
}
fun main(array: Array<String>) {
var student = Student("A同学")
student.name()
student.study()
}
输出:
我还在读书,没有毕业
说明:open字段告诉了我们可以重构。并不代表我们必须重构该方法。除非该方法为抽象方法(abstract关键字)。
如果通过继承得到了多个相同的方法,那么必须重构该方法使用super
关键字去选择性的调用父类的实现。也可以不执行父类的调用。
实例:
//可继承的类,A
open class A {
open fun a() {
println("A_a")
}
open fun b() {
println("A_b")
}
}
//接口对象B, 注意:接口的函数全部默认open。 不用特意添加open
interface B {
fun a() {
println("B_a")
}
fun b() {
println("B_b")
}
}
class C : A(), B {
override fun a() {
super<A>.a() //
super<B>.a() // 全部注销也不报错
}
override fun b() {
super<A>.a()
}
}
fun main(array: Array<String>) {
var c = C()
c.a()
c.b()
}
输出:
A_a
B_a
A_a
Kotlin的继承接口和普通类。两个类如果有相同的函数。在子类之中该函数只会有一个实现。但是需要在函数之中,决定使用哪个父类的实现
注意,Kotlin的类也只能继承一个父类。可以继承多个接口。
重构属性
介绍:其实重构属性,大部分都是为了能重写属性的getter
和setter
。重构属性也一样和重构函数一样。通过open
函数开放属性的权限。通过override
定义属性重构
实例:
open class Foo {
var name: String = ""
open val x: Int = 0
get() = field
}
class Bar1 : Foo() {
override val x: Int = 1
get() {
println(field)
return field
}
}
fun main(array: Array<String>) {
var na = Bar1()
na.x
}
注意:可以用var属性重构一个val属性,但是反之则不行。
因为val只定义了getter方法,重写为var属性时会在子类衍生类之中额外声明一个setting方法。破坏了val定义
接口
介绍:Kotlin之中的接口定义与java类似。也是使用interface
关键字进行定义。
不同于java,kotlin接口类允许方法默认实现。
实例:
interface TestInterface {
fun test1()
fun test2(str: String) {
println("test2:${str}")
}
}
一个类或者对象可以实现多个接口
实例:
interface TestInterface {
fun test1()
fun test2(str: String) {
println("test2:${str}")
}
}
class TT:TestInterface{
override fun test1() {
TODO("重构接口抽象函数")
}
}
相对于继承class,继承interface不用添加构造器。
属性
介绍:Kotlin中接口可以存在属性。但是该属性值不允许初始化。也就是赋值。
interface Test1Interface{
var name:String //抽象的属性
}
class TT1:Test1Interface{
override var name: String = "zz"
}
重构函数
介绍:Kotlin之中的接口对象,可以有已经实现的函数。我们继承之后只有未实现的函数我们需要强制实现。
interface A1 {
fun a1()
fun aa1(){
}
}
interface A2 {
fun a2()
}
class BB:A1,A2{
override fun a1() {
TODO("Not yet implemented")
}
override fun a2() {
TODO("Not yet implemented")
}
}
扩展
介绍:Kotlin可以对一个类的属性和方法进行扩展,并且不需要继承。扩展之后的函数,其他类都可以进行调用。
函数扩展
介绍:可以在已有类之中,添加新的方法。不会对原类做任何修改。
可以对Kotlin所有的类都进行扩展,添加新的函数。
如果类实现的函数,不满足我们的需求,我们可以随意定义,新增扩展使用。
扩展函数的格式为:
fun 类名.扩展函数名(){
实现主体
}
实例1:
class User(var name: String){
}
/**
* 定义的扩展函数
*/
fun User.tuozan() {
print("用户名:$name")
}
fun main(array: Array<String>) {
var user = User("Z同学")
user.tuozan()
}
输出:
用户名:Z同学
实例2:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] //这个this对象就是MutableList对象的实例
this[index1] = this[index2]
this[index2] = tmp
}
fun main(array: Array<String>) {
val te = mutableListOf<Int>(1,2,3)
te.swap(0,2)//将下标0和下标2的值互换
println(te.toString())
}
输出:
[3, 2, 1]
静态解析
介绍:扩展的函数是静态解析的。在调用扩展函数时,具体被调用的是哪个函数是由调用函数的对象表达式来决定的,而不是动态的类型决定的。
实例1:
//定义可继承类Z
open class Z
//定义类D继承于Z
class D : Z()
//给类Z创建一个扩展函数
fun Z.test1() = "Z 的test1函数"
fun main(array: Array<String>) {
var d = D()
print( d.test1())
}
输出:
Z 的test1函数
实例2:
//定义可继承类Z
open class Z
//定义类D继承于Z
class D : Z()
//给类Z创建一个扩展函数
fun Z.test1() = "Z 的test1函数"
fun D.test1() = "D的test1函数"
fun main(array: Array<String>) {
var d = D()
print(d.test1())
}
输出:
D的test1函数
实例3:
//定义可继承类Z
open class Z
//定义类D继承于Z
class D : Z()
//给类Z创建一个扩展函数
fun Z.test1() = "Z 的test1函数"
fun D.test1() = "D的test1函数"
fun ttTest(z: Z) {
println(z.test1())
}
fun main(array: Array<String>) {
var d = D()
println(d.test1())
ttTest(D())
ttTest(Z())
}
输出:
D的test1函数
Z 的test1函数
Z 的test1函数
扩展已有函数
介绍:当创建的扩展函数与成员函数一致,那么编译器执行时会优先使用成员函数。
实例:
class Y {
fun test1() {
println("类的成员函数")
}
}
fun Y.test1() {
println("类的扩展函数")
}
fun main(array: Array<String>) {
var y = Y()
y.test1()
}
输出:
类的成员函数
扩展一个空对象
介绍:在扩展函数里,可以通过this
关键字来判断接收者是否为Null
。这样实现接收者为Null
也可以调用扩展函数。
实例1:
fun main(array: Array<String>) {
var t= null
println(t.toString())
}
输出:
null
实例2:
fun Any?.toString(): String {
if (this == null) return "针对Any Null 进行扩展"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}
fun main(array: Array<String>) {
var t= null
println(t.toString())
}
输出:
针对Any Null 进行扩展
在这种情况下。扩展函数优先执行了。
因为toString()成员函数是需要在非null情况下调用。
而扩展函数添加了?定义可以在非空模式下执行。
例如:
class Y {
fun test1() {
println("类的成员函数")
}
}
fun Y?.test1() {
println("类的扩展函数")
}
fun main(array: Array<String>) {
var y: Y? = null
y.test1()
}
输出:
类的扩展函数
可以在成员函数定义的情况下,我们额外针对空值情况下。调用新的方法
属性扩展
介绍:Kotlin除了函数,也支持对属性的扩展。
注意:扩展属性允许定义在类中,或者kt文件之中。但是不能定义在函数中。
也就是说针对某个类的属性进行扩展。不能在fun 函数里面写
实例:
//正确写法
val <T> List<T>.lastIndex:Int
get() = size-1
fun main(array: Array<String>) {
//错误写法
val <T> kotlin.collections.List<T>.lastIndex:Int
get() = size-1
}
由于扩展属性的定义关系,所以拓展属性没有后端字段,也就是说不能使用field方法。只能使用显式提供的getter和setter方法
实例:
class Zx {
var name: String = "Zx"
}
var Zx.xx: String
get() {
if (name.equals("Zx")) {
return "XXX结果"
}
return ""
}
set(value) {
}
fun main(array: Array<String>) {
var zx = Zx()
println(zx.xx)
}
输出
XXX结果
伴生对象扩展
介绍:如果一个类定义有一个伴生对象,也可以为伴生对象定义函数或者属性的扩展。
伴生对象通过:"类名." 形式调用伴生对象。伴生对象声明的扩展函数,通过类名限定符来调用。
PS:之后会详细介绍伴生对象的概念。现在先让我们简单使用一下。
实例:
class MyClass {
companion object {}
}
//创建伴生对象的扩展函数
fun MyClass.Companion.test() {
println("伴生对象的扩展函数 test1")
}
//创建伴生对象的扩展属性
var MyClass.Companion.Id: Int
get() {
return 12
}
set(value) {
Id = value
}
fun main(array: Array<String>) {
println("ID:${MyClass.Id}")
MyClass.test()
}
输出:
ID:12
伴生对象的扩展函数 test1
作用域扩展
介绍:通常,扩展函数或者属性定义在一个包结构下。
要使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用
可以参考类似于在同包目录下,我们可以直接使用java类不用添加import
。但是在不同包路径我们就需要添加import
扩展也是一样的。
实例:
//文件1
package cn.zinyan.demo.kotlin.demo1
class demo1 {
}
fun demo1.test1(){
println("demo1的扩展函数")
}
//文件2
package cn.zinyan.demo.kotlin.demo2
//添加导入,才能使用
import cn.zinyan.demo.kotlin.demo1.demo1
import cn.zinyan.demo.kotlin.demo1.test1
class demo2 {
}
fun main(array: Array<String>) {
var test= demo1()
test.test1()
}
扩展声明为成员
介绍:在一个类内部,你可以为另一个类声明扩展。
注意:我们说的扩展不能在函数fun
里面声明,但是可以在kt文件里面声明,可以在class里面声明。
在一个扩展中,有个多个隐含的接受者,其中扩展方法定义所在类的实例称为分发接受着,而扩展方法的目标类型的实例称为扩展接受者。
实例1:
class E {
fun testE() {
println("E test1")
}
}
class F {
fun testF() {
println("F test1")
}
//创建E类的扩展函数 test2
fun E.test2() {
testE() //调用E的函数
testF() //调用F的函数
}
fun caller(d: E) {
d.test2()//调用扩展函数
}
}
fun main(array: Array<String>) {
var f = F()
var e = E()
f.caller(e)
}
输出:
E test1
F test1
在上面例子中,F类里面创建了E类的扩展。
此时,F被称为分发接受者。E类被称为扩展接受者。
我们可以看到,在扩展函数中可以调用分发接受者的成员函数。
假如:在调用某一个函数时,该函数在分发接受者和扩展接受者中均存在。则以扩展接受者优先。要引用分发接受者的函数,可以使用限定this语句。
实例2:
class E {
fun bar() {
println("E.Bar")
}
}
class F {
fun bar() {
println("F.Bar")
}
fun E.test3() {
//将会调用E的bar方法
bar()
//将会调用F类,也就是当前类的bar方法
this@F.bar()
}
fun test4(d: E) {
d.test3()
}
}
fun main(array: Array<String>) {
var f = F()
var e = E()
f.test4(e)
}
输出:
E.Bar
F.Bar
以成员的形式定义的扩展函数,可以声明为open。而且可以在子类之中覆盖。
在这类扩展函数的派发过程中,针对分发接受者是虚拟的,但是针对扩展接受者任然是静态的。
实例3:
open class D1
class D2 : D1()
open class E1 {
open fun D1.foo() {
println("D1.foo.in E1")
}
open fun D2.foo(){
println("D2.foo.in E1")
}
fun caller(d:D1){
d.foo()//调用D1的扩展函数
}
}
class E2:E1(){
override fun D1.foo(){
println("D1.foo.in E2")
}
override fun D2.foo(){
println("D2.foo.in E2")
}
}
fun main(array: Array<String>) {
E1().caller(D1())
E2().caller(D1()) //分发接受者虚拟解析。
E1().caller(D2()) //扩展接受者静态解析。
}
输出:
D1.foo.in E1
D1.foo.in E2
D1.foo.in E1
我们可以通过继承,重构父类实现的扩展函数。
到这里,我们就大概了解了扩展的基本情况和普通场景下的使用了。
后续学习和使用之中,再加深理解。
附录
上面例子的Demo代码。
https://zinyan.com/upload/2021/07/MyClass-230930483d2b4f8e989116a6cf9efb5c.kt
评论区