关键字
lateinit :延迟初始化
lateinit var a : String a = "hello" println(" lateinit a = $a");
单一抽象方法,可以使用lambda表达式,无需重写方法,直接在大括号中写对应的实现即可。
fun interface SayHello{ fun say(str:String):String } fun testSam(){ //普通写法 var str = object : SayHello { override fun say(str: String): String { println("$str") return str; } } //lambda 表达式写法 var str2 = SayHello { println("$it") return@SayHello it; } str.say("你好") str2.say("kotlin") }
拓展函数:直接给一个类后面增加点号和一个函数,就能够在别的地方进行调用这个函数。
拓展函数不会修改原来的类,只是通过点号表达式去调用这个方法。调用拓展函数只取决于函数调用时表达式的类型来决定。
如果成员函数和扩展函数相同,那么优先调用成员函数。
fun MutableList .swap(i1:Int,i2:Int){ val tmp = this[i1]; this[i1] = this[i2]; this[i2] = tmp; } fun testExpand(){ val list = mutableListOf(1,2,3,4) list.swap(0,3); println(list) } open class Shape class Rectangle: Shape() fun Shape.getName() = "Shape" fun Rectangle.getName() = "Rectangle" fun printClassName(s: Shape) { println(s.getName()) } printClassName(Rectangle())//会打印 Shape,因为调佣扩展函数时,类型时shape
扩展属性:可以一个类增加属性。但是并没有改变原来的类,只是可以通过点号计算对应的属性。只能显示提供setter、getter,而不能使用构造器。
val List.lastIndext:Int get()= size-1; var list = listOf(11,2,3,4,54) println(list.lastIndext)
伴生对象也可以使用扩展函数和扩展属性。
class MyClass { companion object { } // 将被称为 "Companion" } fun MyClass.Companion.printCompanion() { println("companion") } fun main() { MyClass.printCompanion() }
扩展函数的作用域在当前包里面,如果在其他包里需要引用
package org.example.declarations fun List.getLongestString() { /*……*/} package org.example.usage import org.example.declarations.getLongestString fun main() { val list = listOf("red", "green", "blue") list.getLongestString() }
扩展声明为成员
声明为成员的扩展可以声明为 open
并在子类中覆盖。这意味着这些函数的分发对于分发接收者类型是虚拟的,但对于扩展接收者类型是静态的。
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()) // “Base extension function in BaseCaller” DerivedCaller().call(Base()) // “Base extension function in DerivedCaller”——分发接收者虚拟解析 DerivedCaller().call(Derived()) // “Base extension function in DerivedCaller”——扩展接收者静态解析 }
密封类:要声明一个密封类,需要在类名前面添加 sealed
修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明,密封类是抽象的,不能实例化,密封类的间接继承者可以不用在一个文件里面。
sealed class Expr data class Const(val number: Double) : Expr() data class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber : Expr()
密封列比较好使用when表达式
fun eval(expr: Expr): Double = when(expr) { is Const -> expr.number is Sum -> eval(expr.e1) + eval(expr.e2) NotANumber -> Double.NaN // 不再需要 `else` 子句,因为我们已经覆盖了所有的情况 }
类型投影:Array对应于 Java 的
Array<? super String>
声明处型变:Array对应于 Java 的 Array<? extends Object>
内部类:
标记为 inner 的嵌套类能够访问其外部类的成员。内部类会带有一个对外部类的对象的引用
匿名内部类
window.addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { …… } override fun mouseEntered(e: MouseEvent) { …… } })
枚举
enum class Direction { NORTH, SOUTH, WEST, EAST } enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF) }
伴生对象:
类内部的对象声明可以用 companion 关键字标记:该伴生对象的成员可通过只使用类名作为限定符来调用.可以省略伴生对象的名称,在这种情况下将使用名称 Companion
即使伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员
如果使用 @JvmStatic
注解,你可以将伴生对象的成员生成为真正的静态方法和字段。
class MyClass { companion object Factory { fun create(): MyClass = MyClass() } } val instance = MyClass.create() class MyClass { companion object { } } val x = MyClass.Companion
内联类
的特殊类,它通过在类的前面定义一个 inline
修饰符来声明,内联类必须含有唯一的一个属性在主构造函数中初始化.
委托:委托模式已经证明是实现继承的一个很好的替代方式, 而 Kotlin 可以零样板代码地原生支持它。
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() }
Derived
的超类型列表中的 by-子句表示 b
将会在 Derived
中内部存储, 并且编译器将生成转发给 b
的所有 Base
的方法。委托对象的成员只能访问其自身对接口成员实现.
属性委托:属性的委托不必实现任何的接口,但是需要提供一个 getValue()
函数(与 setValue()
——对于 var 属性)
class Example { var p: String by Delegate() }
标准委托
-
lazy()是接受一个 lambda 并返回一个
Lazy
实例的函数,返回的实例可以作为实现延迟属性的委托
val lazyValue: String by lazy { println("computed!") "Hello" } fun main() { println(lazyValue) println(lazyValue) }
-
Delegates.observable() 接受两个参数:初始值与修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值与新值
class User { var name: String by Delegates.observable("") { prop, old, new -> println("$old -> $new") } } fun main() { val user = User() user.name = "first" user.name = "second" }
委托给另一个属性:为将一个属性委托给另一个属性,应在委托名称中使用恰当的
::
限定符,例如,this::delegate
或MyClass::delegate
。class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) { var delegatedToMember: Int by this::memberInt var delegatedToTopLevel: Int by ::topLevelInt val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt } var MyClass.extDelegated: Int by ::topLevelInt
局部函数:Kotlin 支持局部函数,即一个函数在另一个函数内部,局部函数可以访问外部函数(即闭包)的局部变量,所以在上例中,visited 可以是局部变量
fun dfs(graph: Graph) { val visited = HashSet() fun dfs(current: Vertex) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v) } dfs(graph.vertices[0]) }
高阶函数:高阶函数是将函数用作参数或返回值的函数。
集合:使用的时候再来查。
内置函数let、also、with、run、apply
let 可以使用it代替当前队形,并且返回最后一行表达式结果。
also 可以使用it代替当前对象,返回当前对象。
with 调用对象的时候省略对象名称, 返回最后一行表达式。
run 可以直接调用方法、属性。返回最后一行表达式。
apply 直接调用属性和方法,返回对象。