开始
使用 class 关键字声明一个类,类中包含构造器、初始化块、方法、属性、内部类
kotlin 中没有 new 关键字,直接使用函数形式创建对象
访问修饰符
kotlin 的访问修饰符有 public、protected、private、internal,可用于类、接口、属性、构造器、方法
默认权限都为 public
private 和 internal
private:在同一模块中可见,可跨文件
internal:只在同一文件中可见
构造器
kotlin 的构造器分为主构造器和次构造器,当没有声明构造器时,kotlin 会自动生成一个空参构造
主构造器
主构造器跟在类名之后
1 2 3 4 5 6 7 class Person constructor (name: String, age: Int ,) { } class Person (name: String, age: Int ,) { }
init 块:主构造器中不能包含代码,初始化代码可在 init 中编写,一个类中可以包含多个 init 块
init 块实际是主构造器的一部分,因此主构造器和 init 块先于次构造器执行
1 2 3 4 5 class Person (name: String) { init { println(name) } }
在构造器中传入的参数为形参,类中可以保存参数
1 2 3 4 5 6 7 8 class Person (name: String) { var name: String = name } class Person (var name: String, val age: Int ) { }
次构造器
一个类中可以拥有多个次构造器,使用 constructor 关键字声明
当类不存在主构造器时,一个次构造器可以委托给另一个次构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Person { var name: String = "" var age: Int = 0 var sex: String = "" constructor (name: String) { this .name = name } constructor (name: String, age: Int ) { this .name = name this .age = age } constructor (name: String, age: Int , sex: String) : this (name, age) { this .sex = sex } }
若类存在主构造器,则每个次构造器都需要委托给主构造器
1 2 3 4 5 6 7 8 9 10 11 class Person (var name: String) { var age: Int = 0 constructor (name: String, age: Int ) : this (name) { this .age = age } constructor (name: String, age: Int , salary: Double ) : this (name, age) { } }
属性定义
kotlin 中没有 static 关键字,属性支持访问修饰符、var、val 修饰
1 2 3 4 5 class Person (var name: String, var age: Int ,) { var id: String = "$name :$age " val realAge: Int = age }
属性的初始化时机
kotlin 类中的每个属性必须进行初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Person (var name: String, var age: Int ) { var attr: String = "value1" var attr1: String = "$name :$age " var attr2: String init { attr2 = "value2" } lateinit var attr3: String val attr4: String by lazy { } }
getter、setter
kotlin 会为每个属性自动生成默认的 getter 和 setter,在访问属性 obj.attr
时自动调用 getter 和 setter
val 属性不允许有 setter
在属性声明之后可以设置自定义 getter
使用自定义 getter 后,属性不能通过 lateinit 和 by-lazy 进行初始化
getter 的访问权限与属性的访问权限相同,setter 的访问权限可单独设置
1 2 3 4 5 6 7 8 9 10 class Person (var name: String, var age: String) { var attr: String get () { return "$age " } set (value) { } }
幕后字段 field:用于在 getter 和 setter 中使用当前字段的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person (var age: Int ) { var id: String = "" get () { return field } set (value) { field = value } var id1: String get () { return id1 } }
当只有自定义 getter 时
无论是否使用 field,属性支持直接赋值和 init 块初始化
当只有自定义 setter 时
无论是否使用 field,属性只支持直接赋值初始化
当自定义 getter 和 setter 都存在时
若 getter 和 setter 中都没有使用 field,属性可以不初始化
否则,属性必须直接赋值初始化
简写 getter:对于 getter 可以使用单表达式写法
1 2 3 4 class Person (var name: String) { var id: String = "" get () = "value" }
属性的类型推断:当属性的类型可以从赋值 或简写 getter 中推断时,类型声明可以忽略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Person (var name: String) { var age = "" get () = field var age get () = "" init { age = "..." } var age = "" get () = "" var age get () = field }
继承
kotlin 的所有类都继承 Any 类,Any 类相当于 java 的 Object,但是对 equals、hashCode、toString 三个方法都进行了重写
kotlin 类默认为 final 类,不可继承,类属性默认为不可重载
使用 open 关键字修饰一个类,使该类可以被继承
当子类继承父类时,必须通过父类的构造器构造父类 (主构造器和次构造器都可以 )
当子类存在主构造器时,使用主构造器构造父类
当子类不存在次构造器时,将每个次构造器委托到父类的构造器构造父类
1 2 3 4 5 6 7 8 9 10 11 12 13 open class Person (var name: String) { } class Student (name: String) : Person(name) { } class Teacher : Person { constructor (name: String) : super (name) { } }
子类重写
kotlin 类属性默认为不可重写,子类不可声明父类的同名属性和同名方法
在属性和方法声明时使用 open 关键字和 override 关键字开启重写
1 2 3 4 5 6 7 8 9 10 open class Person (open var name: String) { open fun say () { } } class Student (override var name: String) : Person(name) { override fun say () { } }
super 泛型:用于选择父类或接口的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 open class A { open fun f () { print("A" ) } fun a () { print("a" ) } } interface B { fun f () { print("B" ) } fun b () { print("b" ) } } class C : A () , B{ override fun f () { super <A>.f() super <B>.f() } }
抽象类
kotlin 的抽象类与 java 类似,使用 abstract 关键字,无法实例化,构造器只能由子类调用
1 2 3 4 5 6 7 8 abstract class Person { abstract var age: Int abstract fun say () } class Student : Person () { override var age: Int = 0 override fun say () }
接口
kotlin 接口与 java 接口类似,使用 interface 关键字
接口可以包含属性,属性是抽象的,实现类需要重写属性或在接口中为 val 属性提供 getter
接口可以提供默认实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Listener { val attr: String var attr1: String val attr2: String get () = "value" fun method () { } } class Person : Listener { override val attr: String = "" override var attr1: String = "" }
嵌套类与内部类
嵌套类与内部类都是定义在一个类中的类,区别在于是否持有外部引用,嵌套类不持有外部引用,内部类持有外部引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Outer { private val bar: Int = 1 class Nested { fun foo () = 2 } } class Outer { private val bar: Int = 1 var v = "成员属性" inner class Inner { fun foo () = bar fun innerTest () { var o = this @Outer println("内部类可以引用外部类的成员,例如:" + o.v) } } }
数据类
声明
使用 data 关键字声明,编译器会自动生成 equals、hashCode、toString、ComponentN 和 copy 方法
数据类的主构造器必须包含至少一个参数
属性不能是 open、abstract、sealed、inner
编译器只会对主构造器中声明的属性生成方法 ,对在类体中声明的属性不会提供方法
kotlin 提供了 Pair 和 Triple 数据类
1 data class Person (name: String, age: Int )
解构声明
能将一个对象中的属性同时解构为多个变量,该对象的类需要实现 componentN 操作符,编译器会将解构声明编译为 componentN 函数的调用
解构声明中的参数可以声明类型,支持类型推断,解构声明如果存在不需要的变量,可以使用 _
替换
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person (var name: String, var age: Int ) { operator fun component1 () : String = name operator fun component2 () : Int = age } fun main () { val p = Person("hello" , 20 ) val (name, age) = person val name = person.component1() val age = person.component2() }
返回值解构
可以在返回一个对象的地方使用解构声明替换
1 2 3 4 5 6 7 8 9 for ((key, value) in map) { } data class Result (val result: Int , val status: Status)fun function () : Result { return Result(result, status) } val (result, status) = function()
Lambda 参数解构
在 lambda 表达式的参数部分也可以使用解构声明,一个解构声明看做一个参数,类型声明与推断与一般参数相同
1 2 map.mapValues { entry -> "${entry.value} !" } map.mapValues { (key, value) -> "$value !" }
密封类
使用 sealed 关键字声明,与枚举类类似,密封类的子类必须与密封类位于同一文件或者嵌套在密封类中
密封类的子类可以拥有自己的属性和方法,可以看做是限制子类类型的抽象类 或者扩展的枚举类 ,通常用于 when 匹配
Kotlin 枚举类:与 java 枚举类相同,使用 enum 关键字声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 sealed class Obj class SubClass1 : Obj () { var attr: Int = 0 } class SubClass2 : Obj () { var attr: Int = 0 } sealed class Obj { class SubClass1 : Obj () { var attr: Int = 0 } class SubClass2 : Obj () { var attr: Int = 0 } }
类型别名
使用 typealias 关键字可以为类型、函数创建别名
1 2 typealias NodeSet = Set<Network.Node>typealias FileTable<K> = MutableMap<K, MutableList<File>>
函数别名
1 2 typealias MyHandler = (Int , String, Any) -> Unit typealias Predicate<T> = (T) -> Boolean
内部类和嵌套类别名
1 2 3 4 5 6 7 8 9 class A { inner class Inner } class B { class Nest } typealias AInner = A.Innertypealias BNest = B.Nest
对象表达式
对象表达式用于创建一个匿名对象,使用 object 关键字,对象表达式中可以访问包含它的作用域中的变量
若没有指定继承或实现,匿名对象继承 Any 类
1 2 3 object : <superClass>|<implementation> { }
对象声明
使用 object 的对象声明可以实现单例模式,天然实现了线程安全和延迟初始化 (懒汉式)
对象声明也可以继承和实现接口
1 2 3 4 5 object Singleton { } var s = Singleton Singleton.method()
伴生对象
表示一个伴随着类的对象,在类加载时进行初始化,使用 companion 关键字声明
每个类只能有一个伴生对象 ,伴生对象也可以继承或实现接口,他们依然是实例成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person { companion object { fun method () { } } } class Student { companion object Obj { fun method () { } } } Person.Companion.method() Student.Obj.method() Person.method() Student.method() var p = Personvar s = Student
扩展
能在外部对一个类或接口扩展新的方法和属性,不需要继承或装饰器
扩展函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person fun Person.say () { } class Student <T >fun <T> Student<T> .study () { } fun Person?.say () { } var p: Person = Person()var pn: Person? = Person()p.say() p.speak() pn?.say() pn.speak()
扩展函数静态解析:扩展函数的调用是静态解析的,根据代码的编写静态调用,与运行时无关,若通过一个父类引用调用子类的扩展函数,此时无法调用子类扩展函数,只能调用到父类扩展函数
扩展属性
扩展属性不能拥有幕后字段 field,因此无法直接赋值初始化
1 2 3 4 5 6 7 var Person.name: String get () = "" set (value) { } val Person.age: Int get () = 0
伴生对象扩展
类的伴生对象也支持扩展函数和扩展属性
1 2 3 4 5 6 7 8 9 class Person { companion object {} } fun Person.Companion.method () { } var Person.Companion.attr: Int get () = 0 set (value) {}
类声明扩展
在一个类 A 中声明另一个类 B,可以在类 A 中对类 B 进行扩展,类 A 称为分发接收者,类 B 称为扩展接收者
1 2 3 4 5 6 7 8 9 10 11 12 13 class B class A { var b: B = B() var B.attr: Int get () = 0 set (value) {} fun B.say () { } }
分发接收者虚拟解析:分发接收者的扩展可设为 open,由子类重写,子类对象调用扩展时,会使用子类重写的扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 open class Base { }class Derived : Base () { }open class BaseCaller { open fun Base.printFunctionInfo () { println("Base extension function in BaseCaller" ) } open fun Derived.printFunctionInfo () { println("Derived extension function in BaseCaller" ) } fun call (b: Base ) { b.printFunctionInfo() } } class DerivedCaller : BaseCaller () { override fun Base.printFunctionInfo () { println("Base extension function in DerivedCaller" ) } override fun Derived.printFunctionInfo () { println("Derived extension function in DerivedCaller" ) } } fun main () { BaseCaller().call(Base()) DerivedCaller().call(Base()) DerivedCaller().call(Derived()) }