1 字符串可以包含模板表达式 , 模板表达式以美元符( $ )开头。
val i = 10
val s = "i = $i" // 求值结果为 "i = 10"
2 引用相等
引用相等由 ===(以及其否定形式 !== )操作判断。a === b 当且仅当 a 和 b 指向同一个对象时求值为 true。
3 结构相等
结构相等由 ==(以及其否定形式 != )操作判断;
按照惯例,像 a == b 这样的表达式会翻译成
a?.equals(b) ?: (b === null)
4 幕后字段,field
Kotlin 中类不能有字段。当使自定义定义访问器时,有时有个幕后字段(backing field)有时是必要的。为此 Kotlin 提供一个自动幕后字段,它可通过使用 field 标识符访问;
var counter = 0 // 此初始器值直接写⼊到幕后字段
set(value) {
if (value >= 0)
field = value
}
5 幕后属性
private var _table: Map? = null
public val table: Map
get() {
if (_table == null) {
_table = HashMap() // 类型参数已推断出
}
return _table ?: throw AssertionError("Set to null by another thread")
}
6 惰性初始化属性,lateinit
如果在定义变量时(例如定义成员变量),又想该变量不为空,又不想子定义时就初始化
该修饰符只能在类体中(不是在主构造函数中)声明的 var 属性,并且仅当该属性没有定义 getter 或 setter 时。
该属性必须是非空类型,并且不能是原生类型。
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引⽤
}
}
7 扩展是静态解析
扩展不能真正的修改他们所扩展的类。通过定义一个扩展,你并没有在这个类中插入新成员, 仅仅是可以通过该类型的变量点表达式去调用这个新函数;
如果定义有成员函数和扩展函数,这两个函数有相同的接收者类型、相同的名字并且都适给定的参数,这种情况总是取成员函数。
结果是C
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
可空接收者扩展
可以为可空的接收者类型定义扩展。
fun Any?.toString(): String {
if (this == null) return "null"
// 空检测之后,“this”会⾃动转换为⾮空类型,所以下⾯的 toString()
// 解析为 Any 类的成员函数
return toString()
}
8 扩展属性的行为只能通过明确给定的取值方法与设值方法来定义。
val List.lastIndex: Int
get() = size - 1
9 扩展声明为成员
class D {
fun bar() { …… }
}
class C {
fun baz() { …… }
fun D.foo() {
bar() // 调D.bar,其中的对象成员可以无需通过限定符访问
baz() // 调C.baz
}
}
扩展声明所在的类的实例称为分发接收者,
扩展方法调用所在的接收者类型的实例称为扩展接收者 。
对于分发接收者和扩展接收者的成员名字冲突的情况,扩展接收者优先。
限定的 this语法,可以区分函数
class C {
fun D.foo() {
toString() // 调 D.toString()
[email protected]() // 调C.toString()
}
10 类型的检查与转换
is 和 !is 操作符
智能转换
fun demo(x: Any) {
if (x is String) {
print(x.length) // x 自动转换为字符串
}
}
11 “不安全的”转换操作符---as
如果转换是不可能的,转换操作符会抛出异常
12 “安全的(” 可空)转换操作符--as?
转换失败返回null
13 解构声明
所谓的解构声明就是将一个对象解构(destructure)为多个变量,也就是意味着一个解构声明会一次性创建多个变量.简单的来说,一个解构声明有两个动作:
声明了多个变量
将对象的属性值赋值给相应的变量
可以从一个函数返回多个变量
数据类,
data class Person(var name: String, var age: Int) {
}
当我们对Person的实例使用解构声明时,可以这样做:
var person: Person = Person("Jone", 20)
var (name, age) = person
println("name: $name, age: $age")// 打印:name: Jone, age: 20
对于非数据类,需要自定义componentN
data class Person(var name: String, var age: Int, var addr: String) {
var mobile: String ?= null
operator fun component4(): String {
return this.mobile!!
}
}
14 延迟初始化
val p: String by lazy {
// 计算该字符串
}
15 对一个对象实例调用多个方法( with)
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
val myTurtle = Turtle()
with(myTurtle) { // 画⼀个 100 像素的正⽅形
penDown()
for(i in 1..4) {
forward(100.0)
turn(90.0)
}
penUp()
}