欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Kotlin_02类型系统

程序员文章站 2022-07-12 15:53:43
...

Kotlin类型系统

本文是根据(《Kotlin极简教程》作者:陈光剑)总结摘录而来。

前言

Kotlin是一门强类型、静态类型、支持隐士类型的显式类型语言。

编译时类型与运行时类型

弱类型与强类型

类型系统最主要的作用是,通过检查类型的运算和转换过程,来减少类型错误的发生。如果一个语言的编译器引入越多的类型检查的限制,就可以称这个语言的类型检查越强,反之越弱。根据类型检查的强弱,我们可将编程语言分为:

❑弱类型语言(Weakly checked language)

❑强类型语言(Strongly checked language)

弱类型语言在运行时会隐式做数据类型转换,强类型语言在运行时会确保不会发生未经明确转换(显式调用)的类型转换。但是,强和弱只是相对的。

Kotlin是强类型语言。

静态类型与动态类型

类型检查可发生在编译时期(静态检查)或运行时期(动态检查),这样我们可将编程语言分为:

❑静态类型语言(Statically checked language)

❑动态类型语言(Dynamically Checked language)

静态类型检查是基于编译器来分析源码本身,从而确保类型安全。静态类型检查能让很多bug在编码早期被捕捉到,并且它也能优化运行。因为如果编译器在编译时已经证明程序是类型安全的,就不用在运行时进行动态的类型检查,编译过后的代码会更优化,运行更快。

动态类型语言是在运行时期进行类型标记的检查,因为变量所约束的值,可经由运行路径获得不同的标记。关于动态类型有个很形象的说法:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

——詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-1916)

Kotlin是静态类型语言。

显式类型与隐式类型

还有一种区分方法是,根据变量名是否需要显式给出类型的声明,将语言分为:

❑显式类型语言(Explicitly typed language)

❑隐式类型语言(Implicitly typed language)

前者需要在定义变量时显式给出变量的类型,而后者可以使用类型推论来确定变量的类型。

大多数静态类型语言,例如Java、C/C++都是显式类型语言,但是有些则不是,如Haskell、ML等,它们可以基于变量的操作来推断其类型;Scala是静态类型语言,它使用类型推断功能来支持隐式类型。Kotlin跟Scala类似,它也使用类型推断支持隐式类型。但是,在一些场景下也需要显式声明变量的类型,所以我们可以说,Kotlin同时也是显式类型语言。

显式类型语言与隐式类型语言之间的界限很多时候并不是非显即隐的。当然,事物的本身也往往如此。

结论:Kotlin即是显式类型又是隐式类型

根类型Any

/**
 * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
 */
public open class Any {
    /**
     * Indicates whether some other object is "equal to" this one. Implementations must fulfil the following
     * requirements:
     *
     * * Reflexive: for any non-null reference value x, x.equals(x) should return true.
     * * Symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
     * * Transitive:  for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true
     * * Consistent:  for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
     *
     * Note that the `==` operator in Kotlin code is translated into a call to [equals] when objects on both sides of the
     * operator are not null.
     */
    public open operator fun equals(other: Any?): Boolean

    /**
     * Returns a hash code value for the object.  The general contract of hashCode is:
     *
     * * Whenever it is invoked on the same object more than once, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
     * * If two objects are equal according to the equals() method, then calling the hashCode method on each of the two objects must produce the same integer result.
     */
    public open fun hashCode(): Int

    /**
     * Returns a string representation of the object.
     */
    public open fun toString(): String
}

Kotlin中所有类都有一个共同的超类Any,如果类声明时没有指定超类,则默认为Any。

我们来看一段代码:

val any = Any()
any
[email protected]
any::class
class kotlin.Any
any::class.java
class java.lang.Object

也就是说,Any在运行时,其类型自动映射成java.lang.Object。我们知道,在Java中Object类是所有引用类型的父类。但是不包括基本类型:byte int long等,基本类型对应的包装类是引用类型,其父类是Object。而在Kotlin中,直接统一,即所有类型都是引用类型,统一继承父类Any。

Any是Java的等价Object类。但是与Java不同的是,Kotlin中语言内部的类型和用户定义类型之间,并没有像Java那样划清界限。它们是同一类型层次结构的一部分。
Any只有equals()、hashCode()和toString()个方法。其源码是:

public open class Any {
    public open operator fun equals(other: Any? ): Boolean
    public open fun hashCode(): Int
    public open fun toString(): String
}

从Any的源码注释中,我们可以看到,判断两个对象是否相等,需要满足以下条件:

❑自反性:对于任何非空引用值x, x.equals(x)应返回true。

❑对称性:对于任何非空引用值x和y, x.equals(y)应返回true当且仅当y.equals(x)返回true。

❑传递性:对于任何非空引用值x, y, z,如果x.equals(y)返回true, y.equals(z)返回true,那么x.equals(z)应返回true

❑一致性:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或者始终返回false。

另外,在Kotlin中,操作符==会被编译器翻译成调用equals()函数。

基本类型

数字类型

在Kotlin中,一切皆是对象。所有类型都是引用类型。没有类似Java中的基本类型。但是,可以把Kotlin中对应的这几种基本数据类型,理解为Java的基本类型的装箱类

类型 宽度(Bit)
Byte 8
Short 16
Int 32
Long 64
Float 32
Double 64
注意:
  • 这些类型不能像Java一样(int可以隐式转换成Long)
  • 不支持八进制
  • char不是数字
类型转换

kotin支持显示转换,每个数字类型都有以下方法

  • toDouble(): Double
  • toFloat(): Float
  • toLong(): Long
  • toInt(): Int
  • toChar(): Char
  • toShort(): Short
  • toByte(): Byte

字符

Char

布尔

Boolean

字符串

String

特点:

  1. 字符串的元素——字符可以使用索引运算符s[i]来访问(实现式调用了String的charAt()方法)

    val s="abc"
    println(s[0])
    
    结果:
    a
    
  2. for循环迭代字符串

    for(c in "abc") { println(c)   }
    a
    b
    c
    
  3. 重载+操作符

    可以重载任何对象
    
    >>> "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
    

数组类型

Arrayof(1,2,3)

Kotlin还允许不同类型元素放到一个数组中

val arr = arrayOf(1, "2", true)
>>> arr
[Ljava.lang.Object; @61af1510
>>> arr.forEach{ println(it) }
1
2
true

与Java不同的是,Kotlin中数组不是型变的(invariant)。Kotlin中,我们不能把Array赋值给Array。这地方Kotlin类型检查的限制强于Java的数组类型。

可空类型

Kotlin的类型系统和Java相比,首要的区别就是Kotlin对可空类型的显式支持。

可空类型可以用来标记任何一个变量,来表明这个变量是可空的(Nullable)。例如:Char? 、Int? 、MineType? (自定义的类型)等等

下面这个是可空类型String?。变量str: String?是可空的类型,调用只能通过安全调用(?.)或者非空断言调用(!!.)。

//安全调用?.
fun testNull(str: String?): Int? {
  return str?.length
}

执行:
    testNull(null)
结果:
    null

//非空断言调用!!.
fun testNull(str: String?): Int? {
  return str!!.length
}

执行:
    testNull(null)
结果:
    kotlin.KotlinNullPointerException
    at com.example.carpediem.testapp.Test03Kt.testNull(test03.kt:17)

kotlin.Unit

Kotlin中的Unit类型实现了与Java中的void一样的功能。不同的是,当一个函数没有返回值时,我们用Unit来表示这个特征,而不是null。

kotlin.Nothing

Kotlin中没有Java和C中的相类似函数没有返回值的标记void,但是拥有一个对应Nothing。在Java中,返回void的方法,其返回值void是无法被访问到的

Unit与Nothing之间的区别:Unit类型表达式计算结果的返回类型是Unit。Nothing类型的表达式计算结果是永远不会返回的(跟Java中的void相同)。

类型检测与类型转换

is、!is运算符

is运算符可以检查对象是否与特定的类型兼容(“兼容”的意思是:此对象是该类型或者派生于该类型)。
is运算符用来检查对象(变量)是否属于某数据类型(如Int、String、Boolean等)。类似于instanceof

as运算符

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