一、继承
1. 定义一个Base类:
open class Base(p: Int) {
open var x = 0
open val y = 0
}
定义一个子类Child,继承Base类,要注意,这个子类没有构造函数. 子类没有构造函数,必须在每一个二级构造函数中用super关键字初始化基类,或者在代理另一个构造函数。
class Child(p: Int) : Base(p) {
}
引用Child方式:
val child = Child(100)
再定义一个子类,这个子类有构造函数
2、有构造函数的子类
/**
* 子类没有构造函数
*/
class Child : Base {
/**
* 子类没有够赞函数时,必须在每一个二级构造函数中用super关键字初始化基类,或者在代理另一个构造函数。初始化基类时,
* 可以调用基类的不同构造方法
*/
constructor(p: Int, s: String): super(p) {
}
//父类 x 是 var声明的,子类覆盖时,只能是var
override var x: Int = 10
//父类 y 是val,子类覆盖时,可以是val,也可以用var来声明。
//因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法
override var y: Int = 20
}
因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法 这句话应该好理解,
在父类 val 生命变量的时候,val是不可变的,因此只定义了getter方法,子类覆盖修改后,如果使用了var,var 是可变的,则属性会重新在子类声明setter方法。
二、接口
interface MyInterface {
var name: String //属性,抽象的
var age: Int
fun bar()
fun boo() {
//kotlin的方法体中,可以实现方法
println("boo MyInterface")
}
}
与Java不同的是,kotlin接口可以定义字段
定义一个Child 类 来继承这个接口,注意name、age 与 boo() 方法在子类不同的表现
open class Child(override var name: String) : MyInterface, MyInterface2{
//name 和 age 都是父类接口的属性,必须重写,重写方式有两种,一种在子类构造函数中,一种是如下
override var age: Int
get() = 10
set(value) {}
override fun bar() {
println("Child bar")
}
override fun boo() {
println("子类boo")
super<MyInterface>.boo()
super<MyInterface2>.boo()
}
}
在外部调用Child 测试:(经过构造函数,name 变成了 zhangsan)
与Java不同的是,接口可以实现函数,子类可以覆盖函数的实现,但是覆盖后,父类函数依然有效,如 super<MyInterface>.boo() 的调用,打印出来的就是父类 boo() 方法的结果。
val c = Child("zhangsan")
c.bar()
c.boo()
打印结果如下:
三、扩展
在kotlin里面,扩展是区别Java独有的功能
对一个类的扩展,分为 属性扩展、函数扩展;在函数扩展里面,又有 空函数扩展、函数的静态解析扩展等
1、扩展函数
/**
* 扩展函数
*/
fun extend() {
var c = Child("zhangsan")
c.bar()
c.boo()
//扩展函数
c.FunTest()
//扩展属性
println("扩展属性的值: ${c.lastIndex}")
val d = D("")
printFoo(d) //最终的打印结果是 "Child",而不是"D";因为静态解析的话是根据printFoo 参数进行解析的,参数是C类,因此调用了C类的;若是动态解析,则调用d的方法了
var ch: Child? = null
println(ch.toString())
}
fun Child.FunTest() {
//对类进行扩展,不修改类本身
//扩展函数可以直接访问类的属性及方法
println(name)
//testExtend 是我在Child里面声明的一个函数,在扩展函数里面,可以直接调用被扩展对象的属性、方法
testExtend()
}
如上,我们引用了类 Child,并对Child 进行了函数扩展,扩展出来的一个函数叫做 FunTest(), 在扩展的函数里面,可以直接访问Child 的属性 name, 方法 testExtend()。
使用扩展函数有以下特点:
//对类进行扩展,不修改类本身
//扩展函数可以直接访问类的属性及方法
2、扩展属性:
val Child.lastIndex: Int
get() = 10
对类 Child 进行属性扩展如上,新增属性 lastIndex ,访问值为 10
${c.lastIndex} 其中,$ 代表对其后第一个对象进行引用,如果没有{}, 则认为c 是要引用的对象,${},将{}内容作为了引用对象
3、扩展一个空函数
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。例如:
fun Child?.toString(): String {
if (this == null) return "result is null"
return toString()
}
使用如下,打印结果为: result is null (虽然c 是null,但是扩展的空函数还是被调用并打印出来了信息):
val c = null
print(c.toString())
4、扩展函数是静态解析的
fun Child.foo() = "Child"
如上,先扩展Child 的函数,结果为字符串 “Child”,在外部进行调用:
val c = Child()
c.foo() //最终的打印结果是 "Child"
需要注意的是:扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的: