Kotlin学习(3)可空类型和类型系统

1. 类型系统

使用类型系统,编译器可以检查无意义的、无效的、类型不匹配等错误代码。
另外,静态类型检查还可以提供有用的信息给编译器。

和Java相比,Kotlin去掉了原始类型,只有包装类型,编译器在编译阶段时,让编译器自己去优化性能,把包装类型拆箱成基本类型。
Kotlin引入了可空类型,把有可能为null的值单独用可空类型来表示,这样就可以在可空引用和不可空引用之间划分出一条明确的界限。
kotlin类型层次结构如图3-2所示:
在这里插入图片描述
可以看到,String、Int、MyClass继承了它们的非空类型,也就是说,他们是可以被装箱成 String?、Int?、MyClass?。同时在编译的时候,它们也是可以被拆箱成 string、int、myclass,存储的时候也会存到栈空间。

如果我们显示的定义了可空类型,就会大大降低在编译器在编译时或者运行时的空指针异常。

在kotlin中 ===判断指向的存储空间是否相同&&值是否相同==判断值是否相同,举个例子:

//例子1:
 val a:Int = 1000
 val b:Int = 1000
 >>>a === b 
 true
 >>>a == b
 true

//例子2:
 val a:Int? = 1000
 val b:Int? = 1000
 >>>a == b
 true
 >>>a === b
 flase

第一个例子中,a和b都是Int,在编译的时候拆箱成int, 值“1000”是存在栈空间中,并且它同时有 a、b两个引用,所以a和b指向的存储空间是相同的。
第二个例子中,a和b都是Int?,在编译的时候,会把它们看成是两个不同的对象,在堆中分配了不同的空间,所以它们指向的内存地址是不一样的。

Java中的数组的类型 T[](long[],int[]…}),而在Kotlin中,直接用Array类型表示数组,比如我们构造一个int数组,5个元素,每个元素的初始值为 i*i:

val squareArray = Array(5, {i -> i * i})
>>> squareArray.forEach(::println)
0
1
4
9
16

Kotlin中对Java中8个基本类型数组用了 新的 xxxArray来定义。(比如 BooleanArray、ByteArray、DoubleArray…)

2. 可空类型

Java中的用Optional中的orElse来判空,但是使用起来不美观。
Kotlin中使用了 ?.安全调用符?:Elvis操作符。举一个例子:

fun main(args: Array<String>){
  println(getLength(null))
  println(getLength("hello"))
}

fun getLength(s: String?):Int{
   return s?.length ?: 0       //如果s不为空,就返回 s.length,否则就返回0
}

3. 安全操作符

null的类型是 Nothing?
在Kotlin中,多使用 ?,就可以免去很多判空的操作:

val str:String = null        //编译报错,因为String不能为空
var nullStr:String? = null       //编译通过
null == null                 //返回true
null is Any                  //返回false
null is Any?                 //返回true
var a = null  
>>>a                        //输出null
a = 1                       //编译报错,因为null的类型Nothing? 而1是Int,不能向上转型

我们不能用可空类型来直接调用它的属性或方法,例如下面代码直接报错:

nullStr.length        //编译报错

nullStr?.length       //要用安全调用符 ?. 编译才正确

!!非空断言使得可空类型对象可以调用成员方法或者属性:

nullStr = null
>>>nullStr!!.lenth //如果nullStr为空,则抛出空指针异常
....Expection

4. 特殊类型

4.1 Unit类型

Kotlin中的Unit类型实现了和Java中void一样的功能
下面是Unit的定义

public object Unit{                 //Unit类型是一个object对象类型
  override fun toString() = "kotlin.Unit"  //如果println输出对象类型,就是"kotlin.Unit" 
}

当一个函数没有返回值的时候,我们就用Unit,并且不需要显示的返回Unit、或者声明一个函数的返回类型为Unit
编译器会推断它,所以Unit对我们来说相当是缺省的。

4.2 Nothing与Nothing?类型

在Java中,如果你想让一个函数返回值永远是null,那你可把函数的返回类型写成是 void的装箱类 Void,并返回null
而这个Void就对应Kolin中的Nothing?,其唯一可被访问的返回值也是null
在Kotlin系统类型中,Nothing是最底层,其构造函数时private的,说明其不能被实例化。
**如果一个函数的返回值是Nothing,这代表这个函数永远都不会有返回值。**和Java的void一样

我们可以在返回值为Nothing的函数去抛出异常

Nothing和Unit的区别:
Unit是有返回类型Unit
而Nothing是没有任何返回类型的

Nothing和Nothing?的区别:
Nothing?类型除了 null,其他都不能赋值。
Nothing不能赋值。

4.3 Any与Any?类型

Any?是可空类型层次的根,Any?是Any的父类。

>>> 1 is Any         //Int类型的1是Any
true
>>> 1 is Any?        //Int类型的1是Any?
true
>>>null is Any       //null不是Any类型
false
>>> null is Any?     //null是Any?
true
>>>Any() is Any?     //Any()是Any?类型
true

5. 类型检测与类型转换

在一般情况下,不需要在Kotlin中使用显示转换操作符,因为编译器会用is检查并且在需要时自动转换。

5.1 is运算符

is运算符可以检查对象A是否与特定的类型X兼容(A是X类型或者 是X类型的派生类),和Java中的instanceOf()差不多
在kotlin中,我们可以使用 is,也可以使用 !is

5.2 类型自动转换

在kotlin中的自动转化是这个姿势的:

   fun strlen(ani: Any): Int {
        return when (ani) {
            is String -> ani.length
            is Number -> ani.toString().length
            is Char -> 1
            is Boolean -> 1
            else -> {
                print("Not a string")
                -1
            }
        }
    }
    ...
    
     val len = strlen("abc")
     print(len) //3
     val lens = strlen(1)
     print(lens) //1

5.3 as运算符

as运算符用于执行引用类型的显式类型转化
如果转换类型兼容则转换成功,否则 使用 as? 运算符就会返回null

    open class Foo       //父类Foo
    class Goo : Foo()      //子类Goo
    
    val foo = Foo()
    val goo = Goo()
    foo as Goo  //运行报错,父类型不能强制转化为子类型
    foo as? Goo  //返回null
    goo as Foo //子类型可以转化为父类型

父类强转子类是违反了 里氏替换原则。

发布了248 篇原创文章 · 获赞 99 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/rikkatheworld/article/details/102831276