Kotlin类型系统笔记

Kotlin语言基础笔记

Kotlin流程控制语句笔记

Kotlin操作符重载与中缀表示法笔记

Kotlin扩展函数和扩展属性笔记

Kotlin空指针安全(null-safety)笔记

Kotlin类型系统笔记

Kotlin面向对象编程笔记

Kotlin委托(Delegation)笔记

Kotlin泛型型笔记

Kotlin函数式编程笔记

Kotlin与Java互操作笔记

Kotlin协程笔记

1. 根类型Any

Kotlin中所有的类都有一个共同的基类Any,如果类没有申明继承其他类的话,默认继承的就是Any。我们测试一段代码:

fun main(args: Array<String>) {
    val any = Any()
    println(any)   //打印java.lang.Object@49476842
    println(any::class)  //打印class kotlin.Any
    println(any::class.java)  //打印class java.lang.Object
}

上面输出可以看到其实Kotlin中的Any就是对应Java中的java.lang.Object类型。在Java 中Object类是所有引用类型的父类,但是不包括那些基本类型,int,long,float等。而在Kotlin中所有的类型都是引用类型,统一继承父类Any。

注意,如果你发现println(any::class)打印出这么一段Kotlin reflection is not available,请在你的gradle.gradle文件中加入下面这段依赖compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

Any的源码如下:

Any

它只有三个方法,equals,hashCode和toString。这三个方法跟Java中的意思是一样的,唯一不同的是,Any的equals在Kotlin中进行了操作符重载,所以大家可以使用==来进行值相等的比较。

2. 基本类型

在Java中有byte、int、short、long、float、double、char、boolean这些基本类型,另外,void也可以算是一种基本类型,它也有一个装箱类Void(Koltin中也有类似的概念,Unit、Nothing),Void不能new出来。
在Kotlin中是真正的一切皆是对象。所有的类型都是引用类型。Kotlin中的基本类型的类图如下:

基本类型

2.1 数字(Number)类型

Kotlin 提供了如下的内置类型来表示数字(与 Java 很相近):

类型 宽度(Bit)
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

从上面的类结构图,我们可以看到这些内置的数字类型,都继承了Number和Comparable类。Kotlin的数字类型跟Java的基本相同,但是要注意的是Kotlin中数字没有隐私转化,也就是说Kotlin的Int不能自动转换成Long。另外:Kotlin中字符Char不是数字。Kotlin的这些内部数字类型在运行时会自动转换成Java的基本类型。

数值常量字面值有以下几种
  • 十进制:123
  • Long类型:123L
  • 十六进制:0x0F
  • 二进制:0b000011
  • Double类型:123.123123.123e10
  • Float类型:123.123f123.123F
  • 我们也可以使用下划线来分隔数字:1_000_0001234_5678_9012_3456L0xFF_EC_DE_5E0b11010010_01101001_10010100_10010010
显示转换

范围较小的类型需要显示转换成较大的类型。


image.png

image.png

你需要显示转换类型,如下代码:

fun main(args: Array<String>) {
    val a: Int? = 1
    val g: Long? = a?.toLong()

    val b: Byte = 1
    val i: Int = b.toInt()
}

每个数字类型都继承Number类,而Number中定义了一些方法用来方便的做显示转换。


Number
操作符重载

有时候缺少隐式转换也没关系,例如Long类对+进行了重载。

public operator fun plus(other: Byte): Long
public operator fun plus(other: Short): Long
public operator fun plus(other: Int): Long
public operator fun plus(other: Long): Long
public operator fun plus(other: Float): Float
public operator fun plus(other: Double): Double

2.2 Char字符类型

字符用Char表示。他们不能直接用来当作数字。

image.png

特殊字符可以用反斜杠转义,当然也支持Unicode转义序列语法,例如:\uFF00

2.3 String 字符串类型

索引运算符
fun main(args: Array<String>) {
    val s: String = "abc"
    println(s[2])  //打印c
}

查看源码可以知道,s[i]会被翻译成java.lang.String.charAt()

循环迭代字符串
fun main(args: Array<String>) {
    val s: String = "abc"
    for (c in s) {
        println(c)
    }
}
操作符+重载

字符串String类型重载了+操作符,作用对象是可以任意对象,包括空引用:

>>> "abc".plus(true)
abctrue
>>> "abc"+false
abcfalse
>>> "abc"+1
abc1
>>> "abc"+1.20
abc1.2
>>> "abc"+100L
abc100
>>> "abc"+"cdef"
abccdef
>>> "abc"+null
abcnull
>>> "abc"+'z'
abcz
>>> "abc"+arrayOf(1,2,3,4,5)
abc[Ljava.lang.Integer;@3d6f0054
字符串的值

使用两个双引号定义,其中可以包含一些转义字符。

val s: String = "Hello world, \n\n\n"

还可以使用三个双引号(""")括起来,里面可以包含任意字符。

    val s: String = """
        for (c in "abc")
            print(c)
        """

另外:在kotlin.text包下面的Indent.kt中定义了String类的扩展函数。我们可以使用trimMargin()trimIndent()来去除空格。
trimMargin()默认使用"|"来作为边界字符。

fun main(args: Array<String>) {
    val text = """
     |理论是你知道是这样,但它却不好用。
     |实践是它很好用,但你不知道是为什么。
     |程序员将理论和实践结合到一起:
     |既不好用,也不知道是为什么。
   """
    println(text.trimMargin())
}

输出

理论是你知道是这样,但它却不好用。
实践是它很好用,但你不知道是为什么。
程序员将理论和实践结合到一起:
既不好用,也不知道是为什么。

trimIndent()则是把字符串行的左边空白对其切割:

fun main(args: Array<String>) {
    val text = """
           hello
       world!
   """
    println(text.trimIndent())
}

输出

     hello
world!
字符串模版

字符串中可以包含一些表达式,如下:

fun main(args: Array<String>) {
    val name = "Denny"
    val price = 100.5
    println("My name is $name")  //打印My name is Denny
    println("$name.length is ${name.length}")  //打印Denny.length is 5
    println("""the price is $$price""")  //打印the price is $100.5
}

2.4 Array 数组类型

class Array<T> private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit
    operator fun iterator(): Iterator<T>
    // ……
}

我们可以看到Array重载了[]操作符。
我们可以使用arrayOf()来创建一个数组,并给定数组中的初始值。

fun main(args: Array<String>) {
    val a1 = arrayOf(1, 2, 3)
    println(a1::class)  //打印class kotlin.Array
    println(a1::class.java)  //打印class [Ljava.lang.Integer;

    val a2 = arrayOf(1, "s", null)
    println(a2::class)  //打印class kotlin.Array
    println(a2::class.java)  //打印class [Ljava.lang.Object;
}

我们可以使用arrayOfNulls()来创建一个指定大小,元素都为null的数组,在创建的时候我们必须指定其中元素的类型,要不然会报错。

error

另外:Array类还有一个构造函数。

public inline constructor(size: Int, init: (Int) -> T)

第一个参数是数组的大小,第二个参数是初始化函数类型的参数。比如:

fun main(args: Array<String>) {
    val a = Array(8, { i -> i + i })
    println(a.joinToString(prefix = "[", postfix = "]"))  //打印[0, 2, 4, 6, 8, 10, 12, 14]
}

Kotlin中也有无装箱开销的用来表示原生类型的数组。这些原生数组类型如下:

  • BooleanArray
  • ByteArray
  • CharArray
  • ShortArray
  • IntArray
  • LongArray
  • FloatArray
  • DoubleArray
  • BooleanArray
    这些类和Array没有继承关系,但是他们有同样的函数和属性,他们也有相应的工厂方法:
/**
 * Returns an array containing the specified [Double] numbers.
 */
public fun doubleArrayOf(vararg elements: Double): DoubleArray

/**
 * Returns an array containing the specified [Float] numbers.
 */
public fun floatArrayOf(vararg elements: Float): FloatArray

/**
 * Returns an array containing the specified [Long] numbers.
 */
public fun longArrayOf(vararg elements: Long): LongArray

/**
 * Returns an array containing the specified [Int] numbers.
 */
public fun intArrayOf(vararg elements: Int): IntArray

/**
 * Returns an array containing the specified characters.
 */
public fun charArrayOf(vararg elements: Char): CharArray

/**
 * Returns an array containing the specified [Short] numbers.
 */
public fun shortArrayOf(vararg elements: Short): ShortArray

/**
 * Returns an array containing the specified [Byte] numbers.
 */
public fun byteArrayOf(vararg elements: Byte): ByteArray

/**
 * Returns an array containing the specified boolean values.
 */
public fun booleanArrayOf(vararg elements: Boolean): BooleanArray

3. 可空类型(Nullable Types)

Kotlin把可空性作为类型系统的一部分,这样编译器可以在编译过程中发现一些可能的错误,减少运行过程中抛出异常的可能性。
Kotlin的类型系统和Java相比,主要的区别就是Kotlin对可空类型的显示支持。

3.1 Kotlin中的null

fun main(args: Array<String>) {
    println(null == null)  //打印true
    println(null != null)  //打印false
    println(null is Any)  //打印false
    println(null is Any?)  //打印true
}

与Java不同,Kotlin中null与null是相等的,null不是Any类型,但是是Any?类型。下面我们来看看Null到底是什么类型。

null

编译器告诉我们null的类型是Nothing?

3.2 可空性的实现原理

假如我们有这样一个StringUtil.kt类。

package com.dengyin2000.kotlintest1
object StringUtil{
    fun testNullable1(x: String, y: String?): Int {
        return x.length
    }

    fun testNullable2(x: String, y: String?): Int? {
        return y?.length
    }

    fun testNullable3(x: String, y: String?): Int? {
        return y!!.length
    }
}

然后我们来看看上面的代码对应生成的Java代码是怎样的。在Intellij IDEA中点击"Tools" -> "Kotlin" -> "Show Kotlin Bytecode"。


bytecode

再在出来的界面上点击"Decompile"。


decompile

最后就能看到最终生成的Java代码,代码如下:


finalcode

我们可以看到生成的Java代码,不可为空变量都注解了@NotNull,而可为空变量注解了@Nullable
在函数调用前都用kotlin.jvm.internal.Intrinsics. checkParameterIsNotNull方法检查了不为空变量是否为空。

    public static void checkParameterIsNotNull(Object value, String paramName) {
        if (value == null) {
            throwParameterIsNullException(paramName);
        }
    }

而可空变量的安全调用符y?.length转成Java的代码如下:

y != null?Integer.valueOf(y.length()):null

可空变量的断言调用y!!.length转成的Java代码如下:

      if (y == null) {
         Intrinsics.throwNpe();
      }

      return y.length();

3.3 可空类型层次体系

Any是非空类型的根,Any?是可空类型的根,由于Any?是Any的根,所以Any?是Kotlin的类型层次结构的最顶端。

层次结构

fun main(args: Array<String>) {
    println(1 is Any)  //打印true
    println(1 is Any?)  //打印true
    println(null is Any)  //打印false
    println(null is Any?)  //打印true
    println(Any() is Any?)  //打印true
}

4. kotlin.Unit类型

Kotlin中的Unit类实现了跟Java中void一样的功能,大多数情况下,我们并不需要显示的返回Unit,或者申明一个函数的返回类型为Unit。编译器会推断它。

fun sayHello1() {
}

fun sayHello2() {
    return Unit
}

fun sayHello3(): Unit {
}

fun main(args: Array<String>) {
    println(sayHello1())  //打印kotlin.Unit
    println(sayHello2())  //打印kotlin.Unit
    println(sayHello3())  //打印kotlin.Unit
}

这三个函数其实是一样的。总之这个Unit并没有什么特别之处,看看它的源码:

package kotlin

/**
 * The type with only one value: the Unit object. This type corresponds to the `void` type in Java.
 */
public object Unit {
    override fun toString() = "kotlin.Unit"
}

跟其他的类型一样, Unit的父类是AnyUnit?的父类是Any?

Unit

5. kotlin.Nothing类型

Kotlin中没有类似Java中的返回值为void的标记,在Java中,返回void方法,其返回值是无法被访问到的。void不是变量的类型,但是在Java的包装类中Voidvoid的包装类,如果你想让一个方法返回类型永远是null的话,可以写成如下:

public Void voidDemo() {
    System.out.println("Hello,Void");
    return null;
}

这个Void就是Kotlin中的Nothing?。它的唯一可被访问到的值也是null。
注意:UnitNothing的区别:Unit类型表达式计算结果返回的是Unit类型,Nothing类型表示永远不会返回结果(跟Java的void相同)。
throw关键字中断表达式的计算,并抛出堆栈的功能。所以,一个throw Exception的代码就是返回Nothing的表达式。

fun formatCell(value: Double): String =
    if (value.isNaN()) 
        throw IllegalArgumentException("$value is not a number")  // Nothing
    else 
        value.toString()

再比如,Kotlin的标准库的exitProcess函数:

@file:kotlin.jvm.JvmName("ProcessKt")
@file:kotlin.jvm.JvmVersion
package kotlin.system

/**
 * Terminates the currently running Java Virtual Machine. The
 * argument serves as a status code; by convention, a nonzero status
 * code indicates abnormal termination.
 *
 * This method never returns normally.
 */
@kotlin.internal.InlineOnly
public inline fun exitProcess(status: Int): Nothing {
    System.exit(status)
    throw RuntimeException("System.exit returned normally, while it was supposed to halt JVM.")
}

Nothing?可以只包含一个值:null。


Nothing

6. 类型检测与类型转换

6.1 is运算符

Kotlin的is运算符跟Java中的instanceof是一样的,用来检测某个对象是否某个类型或者父类的实例。其实我们之前已经有用到is运算符了

当你使用is运算符时,类型会自动转换,例如:

open class Animal{
    open fun eat(){
        println("animal eat")
    }
}

open class Dog : Animal() {
    fun run() {
        println("run")
    }
}
class Bird : Animal() {
    fun fly() {
        println("fly")
    }
}

class Fish : Animal() {
    fun swin() {
        println("swin")
    }
}

fun playAnimal(animal: Animal) {
    when (animal) {
        is Dog -> animal.run()  //自动类型转换
        is Bird -> animal.fly()  //自动类型转换
        is Fish -> animal.swin()  //自动类型转换
    }
}

如果是在Java中,我们需要用instanceof判断后然后再显示类型转换(Cast)。

6.2 as运算符

as运算符用于进行显示类型转换,如果转换的类型与指定的类型兼容,转换就能成功,如果类型不兼容,使用as?就会返回null。

package com.dengyin2000.kotlintest1

open class Animal{
    open fun eat(){
        println("animal eat")
    }
}

open class Dog : Animal() {
    fun run() {
        println("run")
    }
}
class Bird : Animal() {
    fun fly() {
        println("fly")
    }
}

class Fish : Animal() {
    fun swin() {
        println("swin")
    }
}

fun main(args: Array<String>) {
    val animal = Animal()

    val dog1 = animal as? Dog
    println(dog1)  //打印null

    val dog = animal as Dog  //Exception in thread "main" java.lang.ClassCastException: com.dengyin2000.kotlintest1.Animal cannot be cast to com.dengyin2000.kotlintest1.Dog
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容