一、类
- Kotlin使用关键字class声明类;
- 类的声明由类名、类头(指定参数类型、主构函数等)和由大括号包围的类体构成;
- 类头和和类体都是可选的,如果没有类体,可以省略花括号;
//1.使用关键字class声明类
class Invoice {
}
//3.类头和类体都都是可选,没有类体可以省略花括号
class Empty
二、构造函数
- 在Kotlin中一个类可以有一个主构造函数和一个或多个次构造函数;
- 主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。如果主构造函数没有任何注解或可见性修饰符,constructor关键字可省略,否则是必须的;
- 主构函数不能包含任何代码,初始化代码放在以init关键字作为前缀的初始化块中;
- 主构函数的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用;
- 与普通属性一样,主构函数中声明的属性也可以是可变(var )或者只读(val );
//2.类名Customer后,主构造函数(firstName: String) ,无注解和可见性修饰符,constructor关键字省略
//5.主构函数中声明属性name是只读的
class Customer(val name: String) {
//3.主构函数无代码,放在init关键字初始化块中
init {
logger.info("Customer initialized with value ${name}")
}
//4.主构函数参数可以类体内声明的属性初始化器中使用,或者初始化块中使用
val customerKey = name.toUpperCase()
}
三、次构造函数
- 次构造函数声明前缀有constructor;
- 如果类有一个主构造函数,每个次构造函数可以使用this关键字,直接委托或者通过别的次构造函数间接委托给主构造函数;
- 如果一个抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数,可见性是public。如果不希望类有一个公有构造函数,需要声明一个带有非默认可见性的主构造函数;
class Person {
//1.次构造函数声明前缀有constructor
constructor(parent: Person) {
parent.children.add(this)
}
}
class Person(val name: String) {
//2.使用this关键字,直接委托给主构造函数
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
//3.主动声明一个公有构造函数,“覆盖 ”没有声明任何主次构造函数默认生成的不带参数的主构造函数
class DontCreateMe private constructor () {
}
四、创建类的实例
- 创建类的实例,像普通函数一样调用构造函数,Kotlin中没有new关键字;
- 类的成员包含:构造函数和初始化块,函数,属性,嵌套类和内部类,对象声明;
//1.像普通函数一样调用构造函数,没有new关键字
val invoice = Invoice()
val customer = Customer("Joe Smith")
五、继承
- 在Kotlin中所有类都有一个共同的超类Any,没有声明超类型声明的都是默认超类Any;
- Any不是java.lang.Object:除了equals()、hashCode()和toString()外没有任何成员;
- 要声明一个显式的超类型,把类型放在类头的冒号之后;
- 如果类有一个主构函数,其基类可以(并且必须)用(基类型的)主构函数参数就地初始化;
- 如果没有主构函数,每个次构函数必须使用super关键字初始化其类型,或委托给一个构造函数初始化;
//1.从 Any 隐式继承
class Example
//2,3.Derived继承超类Base,超类型放在类头的冒号之后
open class Base(p: Int)
//4.Base有一个主构函数,Derived必须使用Base的主构函数初始化
class Derived(p: Int) : Base(p)
//5.View没有主构函数,MyView的次构函数使用super委托给另一个构造函数;
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
Java对比:类上open标注与Java中final相关,它允许其它类从这个类继承。默认情况下,Kotlin所有的类都是final;
六、覆盖方法
- Kotlin与Java不同,需要显示使用open关键字标注可覆盖的成员和使用override关键字覆盖后的成员;
- 标记为override的成员本身是开放的,如果你想禁止再次覆盖,使用final关键字;
//1.open显示标注可继承的类Base,可覆盖的方法v(),否在默认为final
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
//1.override标注覆盖方法,否则编译出错
override fun v() {}
}
open class AnotherDerived() : Base() {
//2.final关键字禁止本身开放的override成员
final override fun v() {}
}
七、覆盖属性
- 和方法覆盖类似,在超类中声明后再派生类中重新声明的属性必须以override开头,并且必须具有兼容类型;
- 每个声明的属性可以由具有初始化器的属性或者具有getter方法的属性覆盖;
- 可以使用一个var属性覆盖一个val属性,反之则不行。因为val本质声明了一个getter方法,覆盖为var只是在子类中额外声明一个setter方法;
open class Foo {
open val x: Int get() { …… }
}
//1.以override开头的x,覆盖了Foo中open开头的属性x
class Bar1 : Foo() {
override val x: Int = ……
}
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
//2.var属性count覆盖了val属性count,本质增加额外的setter方法
override var count: Int = 0
}
八、调用超类实现
- 派生类可以使用super关键字调用超类的函数与属性访问器的实现;
- 在一个内部类访问外部类的超类,可以由外部类名限定的super关键字实现:super@Outer;
open class Foo {
open fun f() { println("Foo.f()") }
open val x: Int get() = 1
}
class Bar : Foo() {
override fun f() {
//1.super关键字调用超类的函数
super.f()
println("Bar.f()")
}
//1.super关键字调用属性访问器
override val x: Int get() = super.x + 1
}
class Bar : Foo() {
override fun f() { /* …… */ }
override val x: Int get() = 0
inner class Baz {
fun g() {
//2.调用 Foo 实现的 f()
super@Bar.f()
//2.使用 Foo 实现的 x 的 getter
println(super@Bar.x)
}
}
}
九、覆盖规则
- 在Kotlin中,如果一个类从它的直接超类继承相同的成员的多个实现,必须覆盖这个成员并提供自己的实现(也许用继承来的其中之一);
- 为了表示采用从哪个超类型继承的实现,使用尖括号中超类型名限定的super;
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
// 接口成员默认就是“open”的
fun f() { print("B") }
fun b() { print("b") }
}
class C() : A(), B {
// 1、2.编译器要求覆盖 f(),分表使用限定符调用A.f()和B.f()
override fun f() {
super<A>.f()
super<B>.f()
}
}
十、抽象类
- 类和其中的某些成员可以声明为abstract,抽象成员在本类中可以不实现,也并不需要open标注抽象类和函数;
- 可以用一个抽象成员覆盖一个非抽象的开放成员;
open class Base {
open fun f() {}
}
//1.Derived类使用abstract声明为抽象类
abstract class Derived : Base() {
//2.用一个抽象成员覆盖一个非抽象的开放成员
override abstract fun f()
}
十一、伴生对象
Java对比:Kotlin中类没有静态方法,大多数情况下,建议简单的使用包级函数。或在类内声明一个伴生对象,就可以像Java/C#调用静态方法相同的语法来调用其它成员,只使用类名作为限定符;
1.新技术,新未来!尽在1024工场。时刻关注最前沿技术资讯,发布最棒技术博文!(甭客气!尽情的扫描或者长按!)
2.加入“Kotlin开发”QQ讨论群,一起学习一起Hi。(甭客气!尽情的扫描或者长按!)