这篇文章是我在学习和使用Kotlin时的一些总结和体会,包括一些代码用来更清晰的帮助大家学习!
1、对象相关
对象表达式:相当于Java匿名类部类,在使用的地方被立即执行:
val a = 10
val listener = object : Info("submit"),IClickListener {
override fun doClick() {
println("a:$a")
}
}
listener.doClick() // 打印 a:10
//有时候我们只是需要一个没有父类的对象,我们可以这样写:
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
//像 java 的匿名内部类一样,对象表达式可以访问闭合范围内的变量 (和 java 不一样的是,这些变量不用是 final 修饰的)
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent){
enterCount++
}
})
}
对象申明:Kotlin 中我们可以方便的通过对象声明来获得一个单例,对象声明是延迟加载的, 在第一次使用的时候被初始化,对象声明不是一个表达式,不能用在赋值语句的右边,对象声明不能在局部作用域(即直接嵌套在函数内部),但是它们可以嵌套到其他对象声明或非内部类中,
object MyInfo: Info("submit"),IClickListener {
override fun doClick() {
println("MyInfo do click, $text") // Log: MyInfo do click, , submit
}
}
fun main(args: Array<String>) {
MyInfo.doClick()
}
//当对象声明在另一个类的内部时,这个类的实例并不能直接访问对象申明内部,而只能通过类名来访问,同样该对象也不能直接访问到外部类的方法和变量
class Site {
var name = "菜鸟教程"
object DeskTop{
var url = "www.runoob.com"
fun showName(){
print{"desk legs $name"} // 错误,不能访问到外部类的方法和变量
}
}
}
fun main(args: Array<String>) {
var site = Site()
site.DeskTop.url // 错误,不能通过外部类的实例访问到该对象
Site.DeskTop.url // 正确
}
伴随(生)对象:相当于静态内部类+该类的静态属性,所在的类被加载,伴生对象被初始化
class Books(var name: String, val page: Int) {
companion object ComBooks{
val a : Int = 10
fun doNote() {
println("do note")
}
}
}
fun main(args: Array<String>) {
Books.ComBooks.doNote()
println("Book.a = ${Books.ComBooks.a}")
println("-------------")
Books.doNote()
}
// Log
do note
Book.a = 10
-------------
do note
//伴随对象的成员可以通过类名做限定词直接使用:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
//在使用了 companion 关键字时,伴随对象的名字可以省略:
class MyClass {
companion object {
}
}
//尽管伴随对象的成员很像其它语言中的静态成员,但在运行时它们任然是真正类的成员实例,比如可以实现接口:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
//如果你在 JVM 上使用 @JvmStatic 注解,你可以有多个伴随对象生成为真实的静态方法和属性
2、属性字段相关
备用字段:Kotlin中不能有field,但在自定义getter/setter的时候需要直接访问属性而不是又通过getter/settter来取值赋值(循环调用)。Kotlin自动提供一个备用字段(field),通过它可以直接访问属性,没有使用备用字段时不生成备用字段(使用了setter就会生成)
//使用field关键字
public var fieldProp = ""
get() = field
set(value) {
field = value;
}
//不生成:
val isEmpty: Boolean
get() = this.size == 0
//生成:
val Foo.bar = 1
备用属性:功能与备用字段类似。:
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // 参数类型是自动推导
}
return _table ?: throw AssertionError("Set to null by another thread")
}
Kotlin可以像python(@property)一样把方法变成属性调用,Kotlin是定义一个属性复写get()方法返回某个对象中其他的计算出来的值。
3、编译时常量
相当于java static finial xxx,而val 只是fInal ,一个编译时常量,一个运行时常量。使用const必须:
在kt文件中(类之外,Top-level)或在object{}中
必须是基本类型或String
必须没有自定义getter
4、代理模式
类代理:
Kotlin 在语法上支持代理 ,Derived 类可以继承 Base 接口并且指定一个对象代理它全部的公共方法:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { printz(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
//在 Derived 的父类列表中的 by 从句会将 b 存储在 Derived 内部对象,并且编译器会生成 Base 的所有方法并转给 b
代理属性:
所谓的委托属性,就是对其属性值的操作不再依赖于其自身的getter()/setter()方法,是将其托付给一个代理类,从而每个使用类中的该属性可以通过代理类统一管理,再也不用在每个类中,对其声明重复的操作方法。语法:
val/var <property name>: <Type> by <expression>
//var/val:属性类型(可变/只读)
//name:属性名称
//Type:属性的数据类型
//expression:代理类
使用场景:
延迟加载属性(lazy property): 属性值只在初次访问时才会计算
可观察属性(observable property): 属性发生变化时, 可以向监听器发送通知
将多个属性保存在一个 map 内, 而不是保存在多个独立的域内
Kotlin标准库中已实现的代理:
延迟加载(Lazy):lazy()是一个函数, 接受一个Lambda表达式作为参数, 返回一个Lazy类型的实例,这个实例可以作为一个委托, 实现延迟加载属性(lazy property): 第一次调用 get() 时, 将会执行 lazy() 函数受到的Lambda 表达式,然后会记住这次执行的结果, 以后所有对 get() 的调用都只会简单地返回以前记住的结果:
val no: Int by lazy {
200
}
val c = 200
fun main(args: Array<String>) {
val b = 200
println(no) // Log : 200
println(no) // Log : 200
}
注意:
var类型属性不能设置为延迟加载属性,因为在lazy中并没有setValue(…)方法
lazy操作符是线程安全的。如果在不考虑多线程问题或者想提高更多的性能,也可以使用 lazy(LazyThreadSafeMode.NONE){ … },lazy的三个参数为:
SYNCHRONIZED:锁定,用于确保只有一个线程可以初始化[Lazy]实例。
PUBLICATION:初始化函数可以在并发访问未初始化的[Lazy]实例值时调用几次,,但只有第一个返回的值将被用作[Lazy]实例的值。
NONE:没有锁用于同步对[Lazy]实例值的访问; 如果从多个线程访问实例,是线程不安全的。此模式应仅在高性能至关重要,并且[Lazy]实例被保证永远不会从多个线程初始化时使用。