学习kotlin第15天_操作符重载、空安全、异常、注解

时间:2021-12-13 15:29:23

继续跟着官方文档学习,再也不说kotlin坑了,感觉坑的是我而不是kotlin。

1、操作符的重载介绍了一些操作符的执行过程以及对应的函数,要是我把文档抄一遍似乎没啥意义。我的理解是操作符会被当作对应的函数来调用。特殊地,赋值在kotlin中不是表达式。“null == null”总返回true,如果x已经定义了不可为空,那么“x  == null”总为为false而不调用equals(废话.....),此外,命名函数的中缀调用在前面(第11篇文章)已经介绍过了。

2java中,任何引用都可以是null,而调用null对象会产生NullPointerException(简称NPE),而kotlin的语言设计想要消除该异常。首先得知道这个异常产生的几种情况,

(1)显式调用 throw NullPointerException()

(2)使用了下文描述的 !! 操作符

(3)外部 Java 代码导致的

(4)对于初始化,有一些数据不一致(如一个未初始化的 this用于构造函数的某个地方)。

kotlin中,类型系统会自动识别一个引用能否为空。例如:字符串类型如果不加问号声明为可空,则无法将其赋值为null。类似地,如果已经声明了一个字符串为可空,那么不能调用length属性,因为它是不安全的。

3、那么问题来了,对于一个可空对象,我们如何引用呢?办法一、使用if...else进行非空判断,当然要注意该对象是不可变的,否则判断后又变为null了就白费了。

4、既然有办法一自然有办法二,叫做安全调用,就是在调用时加个?”表示是否为空?为null则返回“”null””如果想要忽略null,可以与let合用。

fun main(args: Array<String>) {
var s: String? = null
println(s?.length)
s?.let { println(it) }
}

5Elvis操作符使用“?:”表示,如果左侧非空则执行左侧表达式,否则返回右侧表达式的值。

fun main(args: Array<String>) {
var s: String? = null
println(s?.length ?: -1)
}

6!!”操作符用于显示获取NPE异常,如果不加的话连编译都不通过。

fun main(args: Array<String>) {
var s: String? = null
println(s.length)
// println(s!!.length)
}

7、基本类型的转换使用其to*方法,而其它类型的转换使用as时有可能造成ClassCastException,所以使用安全的类型转换,即在as前加一个问号。

open class A() {}
fun main(args: Array<String>) {
val a: A = A()
val b: Long = 1
val aInt: Int? = a as? Int
val bInt: Int? = b.toInt()
println(aInt)
println(bInt)
val cInt: Int? = a as Int
println(cInt)
}

8、对于可空集合可以用filterNotNull方法来过滤掉非空元素

fun main(args: Array<String>) {
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()
intList.forEach { print(it) }//输出124
}

9、类似于javakotlin中所有异常类都是Throwable的子孙类,使用throw抛出异常,使用try...catch...finally...捕获异常。try是一个表达式,返回值由try块或者catch块最后的表达式决定,与finally无关。

fun main(args: Array<String>) {
val a: Int? = try {
parseInt("12")
} catch (e: NumberFormatException) {
null
}
println(a)
val b: Int? = try {
parseInt("12.5")
} catch (e: NumberFormatException) {
null
}
println(b)
}

10kotlin没有受检的异常。。。不懂。

11throw也是表达式,所以可以作为Elvis表达式的一部分。

data class Person(val name: String?)

fun main(args: Array<String>) {
val person = Person(null)
val s = person.name ?: throw IllegalArgumentException("Name required")
println(s)
}

12throw表达式的类型是特殊类型Nothing。该类型没有值,而是用于标记永远不可能达到的代码位置。如下:fail没有返回值。

data class Person(val name: String?)
fun fail(message: String): Nothing {
throw IllegalArgumentException(message)
}
fun main(args: Array<String>) {
val person = Person(null)
val s = person.name ?: fail("Name required")
println(s) // 在此已知“s”已初始化
}

13注解是将元数据附加到代码的方法不懂),使用annotation声明。。。

注解的附加属性可以通过用元注解标注注解类来指定一脸懵逼,不懂,怎么看起来有点像标签)。管他的,先记住再说。

对主构造函数注解时必须有constructor关键字。

注解也可以标注属性访问器。

注解可以带参数,但参数不能可空。

注解可以作为参数,但此时不带“@”。

类作为注解参数时,需要使用kotlin类。

注解可用于lambda表达式体的invoke方法上。

属性或者主构造函数参数可以精确标记注解。

对于同一目标有多个注解的情况。可以在目标后添加方括号并把注解放在里面。

支持的使用处目标的完整列表如下:

file(标注整个文件,需要放在文件顶层、package之前)

property (具有此目标的注解对 Java 不可见)

field(标注java字段)

get (属性 getter)

set (属性 setter)

receiver (扩展函数或属性的接收者参数)

param (构造函数参数)

setparam (属性 setter 参数)

delegate (为委托属性存储其委托实例的字段)

14java注解与kotlin注解100%兼容。

调用java注解时,需要使用命名参数语法。

特殊地。value参数的值无需显示指定。如果value具有数组类型,kotlin中会成为vararg可变数量参数。

//java
public @interface AnnWithValue {
String[] value();
}
// Kotlin
@AnnWithValue("abc","def") class C

对于其它数组型参数,需要显示使用arrayOf。

//java
public @interface AnnWithValue {
String[] v();
}
// Kotlin
@AnnWithValue(v= arrayOf("abc","def")) class C

15、注解实例的值会作为属性暴露给kotlin代码。

// Java
public @interface AnnWithValue {
int value();
}
// Kotlin
fun foo(annWithValue: AnnWithValue ) {
val i = annWithValue.value
}

 今天就到这里吧。。。继续去copy和paste去了