1.修饰符
1.类修饰符
- abstract 抽象类
- final 不可被继承
- enum 枚举类
- open 可继承
- annotation 注解类
- sealed 密封类
- data 数据类
2.成员修饰符
- override 重写函数
- open 可被重写
- final 不可被重写
- abstract 抽象函数
- lateinit 后期初始化
3.访问权限控制
- private
- protected
- public 默认
- internal 整个模块内可访问
4.泛型可变性
2.this关键字
- this关键字持有当前对象的引用。可以使用this来引用变量或者成员函数,也可以使用return this来返回某个类的引用
- 在类的成员中,this指向的是该类的当前对象
- 在扩展函数或者带接收者的函数字面值中,this表示在点左侧传递的接收者参数,例如:
val sum = fun Int.(x:Int):Int = this + x
println(1.sum(1)) //2
- 如果this没有限定符,指的是最内层的包含它的作用域。如果想要引用其他作用域中的this,可以使用this@label标签:
class Outer {
val oh = "OH!"
inner class Inner {
fun m() {
val outer = this@Outer
val inner = this@Inner
println("outer=" + outer)
println("inner=" + inner)
println("this=" + this)
println(outer.oh)
val fun1 = hello@ fun String.() {
val obj1 = this //fun1的接收者
println("obj1=" + obj1)
}
val fun2 = { _: String ->
val obj2 = this
println("obj2=" + obj2)
}
"abc".fun1()
fun2("def")
}
}
}
fun main(args: Array) {
val outer = Outer()
outer.Inner().m()
}
//输出:
//outer=com.lpcoder.agile.base.Outer@3764951d
//inner=com.lpcoder.agile.base.Outer$Inner@4b1210ee
//this=com.lpcoder.agile.base.Outer$Inner@4b1210ee
//OH!
//obj1=abc
//obj2=com.lpcoder.agile.base.Outer$Inner@4b1210ee
3.操作符和操作符重载
1.操作符优先级
2.operator和infix修饰符
- 重载操作符的函数需要用operator修饰符标记。中缀操作符的函数使用infix修饰符标记
3.一元操作符
1.前缀操作符
表达式 |
翻译为 |
+a |
a.unaryPlus() |
-a |
a.unaryMinus() |
!a |
a.not() |
- 确定a的类型,令其为T
- 为接收者T查找一个带有operator修饰符的无参函数unaryPlus(),即成员函数或扩展函数
- 如果函数不存在或不明确,则导致编译错误
- 如果函数存在且其返回类型为R,则表达式+a具有类型R
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
fun main(args: Array) {
println(-Point(1, 1)) //Point(x=-1, y=-1)
}
2.递增和递减
表达式 |
翻译为 |
a++ |
a.inc()返回值是a |
a-- |
a.dec()返回值是a |
++a |
a.inc()返回值是a+1 |
--a |
a.dec()返回值是a-1 |
4.二元操作符
1.算数运算符
表达式 |
翻译为 |
a+b |
a.plus(b) |
a-b |
a.minus(b) |
a*b |
a.times(b) |
a/b |
a.div(b) |
a%b |
a.rem(b)、a.mod(b) |
a..b |
a.rangeTo(b) |
2.字符串的+运算符重载
"" + 1 // 1
1 + "" // error
1.toString() + "" // 1
3.自定义重载的+运算符
data class Counter(var index: Int)
operator fun Counter.plus(increment: Int): Counter {
return Counter(index + increment)
}
fun main(args: Array) {
println(Counter(5) + 10) //Counter(index=15)
}
4.in操作符
表达式 |
翻译为 |
a in b |
b.contains(a) |
a !in b |
!b.contains(a) |
5.索引访问操作符
表达式 |
翻译为 |
a[i] |
a.get(i) |
a[i]=b |
a.set(i,b) |
6.调用操作符
表达式 |
翻译为 |
a() |
a.invoke() |
a(i) |
a.invoke(i) |
7.计算并赋值
表达式 |
翻译为 |
a+=b |
a.plusAssign(b) |
a-=b |
a.minusAssign(b) |
a*=b |
a.timesAssign(b) |
a/=b |
a.divAssign(b) |
a%=b |
a.modAssign(b) |
8.相等与不等操作符
- 引用相等(两个引用指向同一个对象):===、 !==
- 结构相等(使用equals()判断):==、!=
表达式 |
翻译为 |
a==b |
a?.equals(b)?:(b===null) |
a!=b |
!(a?.equals(b)?:(b===null)) |
- 当与null显式比较时,a==null会被自动转换为a===null
- ===和!==不可重载
9.Elvis操作符 ?:
- Elvis操作符是特定用来跟null比较,用来做null安全检查的
- y=x?:0 等价于 y= if (x !== null) x else 0
10.比较操作符
- 所有的比较都转换为对compareTo的调用,这个函数需要返回Int值
表达式 |
翻译为 |
a>b |
a.comapreTo(b)>0 |
a
| a.comapreTo(b)<0 |
a>=b |
a.comapreTo(b)>=0 |
a<=b |
a.comapreTo(b)<=0 |
11.用infix函数自定义中缀操作符
data class Person(val name: String, val age: Int)
infix fun Person.grow(years: Int): Person {
return Person(name, age + years)
}
fun main(args: Array) {
println(Person("Jack", 20).grow(2)) //Person(name=Jack, age=22)
println(Person("Jack", 20) grow 2) //Person(name=Jack, age=22)
}
4.扩展函数和扩展属性
// 扩展函数
fun String.notEmpty(): Boolean {
return !this.isEmpty()
}
// 扩展属性
val List.lastIndex: Int get() = size - 1
fun main(args: Array) {
println("".notEmpty()) //false
val list = listOf(1,2,3)
println(list.lastIndex) //2
}
1.扩展函数
- 声明一个扩展函数需要用被扩展的类型做为前缀
- 扩展不是真正的修改所扩展的类,仅仅是通过该类型的变量,用点(.)表达式去调用这个新函数
- 示例:为MutableList添加一个swap函数
fun MutableList.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
fun main(args: Array) {
val list = mutableListOf("a", "b", "c", "d", "e")
list.swap(0, 4)
println(list) //[e, b, c, d, a]
}
2.扩展属性
- 由于扩展没有实际地将成员插入类中,因此对于扩展的属性来说,它的行为只能由显式提供的getters/setters定义
- 示例:
val List.lastIndex: Int get() = size -1
5.空指针安全
- 显式调用throw NullPointerException()
- 使用了!!操作符
- 调用的外部Java代码有NPE
- 对于初始化,有一些数据不一致(如一个未初始化的this用于构造函数的某个地方)
var a = "abc"
a = null //ERROR
- 如果要允许为null,我们可以在变量的类型后面加个问号?,声明一个变量为可空的:
var a:String? = "abc"
a = null
- 如果我们声明了一个可空String?类型变量a,然后直接调用length属性,这将是不安全的。编译器会直接报错。正确的做法是使用安全调用(?.)和非空断言调用(!!.)
var a:String? = "abc"
a = null
a?.length //null
a!!.length //NPE
- 安全调用在链式调用中很有用。在调用链中如果任意一个属性(环节)为空,这个链式调用就会安全返回null
- 如果只对非空值执行某个操作,安全调用操作符可以与let(以调用者的值作为参数来执行指定的函数块,并返回其结果)一起使用:
fun main(args: Array) {
val listWithNulls: List = listOf("A", "B", null)
println(listWithNulls)
listWithNulls.forEach { it?.let { println(it) } }
//[A, B, null]
//A
//B
}
END
参考资料:《Kotlin极简教程》