Kotlin 类与对象 —— 类(一) | Ohmer's Blog

JerryXia 发表于 , 阅读 (0)
类的声明

kotlin用关键字class声明,声明一个类可以只声明头的部分,类的声明默认是final的。就像:

1
2
3
class Persion
//或者
class Person(name: String)

类的构造函数可以有很多,但是只有一个可以声明在类的头部,这个构造函数被称做“主构造函数”,其他的被称做“次构造函数”。构造函数使用constructor关键字,主构造函数在没有可见性修饰符和注解的情况下,constructor可以被省略。默认的情况下,所有的构造函数的可见性都是public,对于使用方来说是与类的可见性保持一致。

1
class Customer public @Inject constructor(name: String) { ... }

主构造函数没有自己的函数体,它的参数可以在类的初始化块(init修饰)访问,在类的属性初始化时也可以访问。所以,在主构造函数里面想要做的事情,可以放在类的初始化块中实现。

1
2
3
4
5
6
class Customer(name: String) {
val customerKey = name.toUpperCase()
init {
print("Customer initialized with value ${name}")
}
}

如果我们想要在类的全局都可以访问主构造函数的参数,可以在参数前面加上val或者var,这样主构造函数的参数就和类的属性一样了。

1
2
3
4
5
6
7
8
open class Person(val name: String,val age: Int){
fun showName(){
print("my name is $name")
}
open fun showAge(){
print("my age is $age")
}
}

次构造函数必须要使用constructor修饰,并且必须直接或者间接的委托给主构造函数。

1
2
3
4
5
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}

继承

类的继承

kotlin中所有的类都有一个父类Any,类似于java中的Object,但不存在对等关系。Any中只有equals()hashCode()toString()三个方法,所以其他的Object的方法都不能直接调用。详情请参见Java interoperability。后面有机会我们会再讲到。

kotlin默认类都是final的,为了可以被继承,我们需要在类的声明前面加上open,让该类可以被其他类继承。

1
open class Base(p: Int)

如果父类有主构造函数的话,则必须在子类声明的头部被初始化。

1
class Derived(p: Int) : Base(p)

次构造函数也必须直接或间接的初始化父类的构造函数:

1
2
3
4
5
6
7
class MyView : View {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
}
}

复写方法

同类一样,子类只能复写父类中被open修饰的函数,复写方法必须使用override

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
open class Person(name: String, age: Int){
var name = name;
val age = age;
fun showName(){
print("my name is $name")
}
open fun showAge(){
print("my age is $age")
}
}
class Women(name: String, age: Int) : Person(name, age){
override fun showAge(){
print("my age is 18")
}
override fun showName(){
// error, compiler complain
}
}

子类中复写的方法,默认也是open的,如果需要,可以在方法override之前添加final注解,让该子类的子类不能再复写该方法。

1
final override fun v() {}

多继承

kotlin和java8一样,本身并不能同时继承于多个类,但是可以实现多个接口,而且接口可以有自己的实现,所以当父类和接口或者接口和接口中的方法一样时,会发生冲突,我们需要明确这个时候的解决方案,不然就会compiler complain

1
2
3
4
5
6
7
8
9
10
11
interface Young{
fun showAge(){
print("my age is between 13 and 25")
}
}
class Student(name: String, age: Int) : Person(name, age), Young{
override fun showAge() {
super<Person>.showAge()
super<Young>.showAge()
}
}

委托

委托被认为是一个非常好的替代继承和实现的设计模式,kotlin也支持这种模式。

1
2
3
4
5
6
7
8
9
10
11
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}

在类的声明过程中,在父类类型后面使用by关键字指明在Derived的对象中将会内部存储b对象,并且编译器会将Base的所有方法指向b