快捷搜索:  as

Kotlin极简教程:第4章 基本数据类型与类型系统

原文链接:https://github.com/EasyKotlin

到今朝为止,我们已经懂得了Kotlin的基础符号以及根基语法。我们可以看出,应用Kotlin写的代码更简洁、可读性更好、更富有临盆力。

本章我们来进修一下Kotlin的基础数据类型与类型系统。

道生一,平生二,二生三,三生万物 (老子《道德经》第四十二章)

在谋略机科学中,最早的类型系统用来差别数字里面的整数和浮点数。

在20世纪五六十年代,这种分类扩展到了布局化的数据和高阶函数中。

70年代,引入了几个更为富厚的观点,例如:参数化类型,抽象数据类型,模块系统,子类型等等,类型系统作为一个自力的领域形成了。

在每一门编程说话中,都有一个特定的类型系统(Type System)。类型系统是一门编程说话最核心也是最根基的部分。我们这里说的类型系统,可以简单理解为以下两个部分:

一组基础类型构成的PTS(Primary Type Set,基础类型聚拢);

PTS上定义的一系列组合、运算、转换规则等。

这一简单优雅而惊人的天下构成不雅,贯穿了人类现实天下和谋略机编程说话所定义的虚拟天下。或许说话的设计者也没有意料到,然则终极的结果确凿是有限的设计导出了无限的可能性。

本章我们将进修Kotlin说话的基础类型,以及简单先容Kotlin的类型系统。

4.1 什么是类型?

统统皆是映射

在谋略机中,任何数值都因此一组比特(01)组成的,硬件无法区分内存地址、脚本、字符、整数、以及浮点数。这个时刻,我们应用类型付与一组比特以特定的意义。

类型(Type),本色上便是内存中的数值或变量工具的逻辑映射。

《周易》有云:

易有太极,是生两仪,两仪生四象,四象生八卦。(《易传·系辞上传》) 。

这里所包孕的思惟,跟我们这里所说的类型系统的思惟有着异曲同工之妙。

类型系统用于定义若何将编程说话中的数值和表达式归类为许多不合的类型,若何操作这些类型,这些类型若何相互感化等。

类型系统在各类说话之间有异常大年夜的不合,主要的差异存在于编译时期的语法,以及运行时期的操作实现要领。

类型系统供给的主要功能有:

安然性

编译器可以应用类型来反省无意义的,或者是可能无效的代码。例如,在强类型的说话中,假如没有对字符串的+进行重载,那么表达式

"Hello, World" + 3

就会被编译器检测出来,由于不能对字符串加上一个整数。强类型供给更多的安然性。

然则,为了让法度榜样员可以写出极简的代码,很多说话都供给了操作符重载的机制。比如说,在Scala中,上面的代码是可以被精确履行的(重载了+操作符)

scala> "Hello,World"+1

res15: String = Hello,World1

scala> 1+"Hello,World"

res16: String = 1Hello,World

然则在Kotlin中, 因为Int类型没有对+实现重载,以是环境是这样

>>> "Hello,World"+1

Hello,World1

>>> 1+"Hello,World"

error: none of the following functions can be called with the arguments supplied:

public final operator fun plus(other: Byte): Int defined in kotlin.Int

public final operator fun plus(other: Double): Double defined in kotlin.Int

public final operator fun plus(other: Float): Float defined in kotlin.Int

public final operator fun plus(other: Int): Int defined in kotlin.Int

public final operator fun plus(other: Long): Long defined in kotlin.Int

public final operator fun plus(other: Short): Int defined in kotlin.Int

1+"Hello,World"

^

最优化

静态类型反省可供给有用的信息给编译器。编译器可以应用更有效率的机械指令,实现编译器优化。

可读性

抽象化(或模块化)

类型本色上是对较低层次的逻辑单元进行高层次的逻辑抽象。这样我们就可以直接应用类型在较高层次的要领思虑,而不是繁重的低层次实现。

例如,我们可以将字符串想成一个值,以此取代仅仅是字节的数组。字符串便是一个抽象数据类型。

从01到类型,从类型到接口API,再到软件办事,都可以看做是广义的“类型”范畴。

法度榜样中的变量在法度榜样履行时代,可能会有不合的取值范围,我们可以把变量可取值的最大年夜范围称为这个变量的类型。例如,具有类型Boolean的变量x,在法度榜样履行时代,只能取布尔值。指定变量类型的法度榜样设计说话,称为类型化的说话(typed language)。

假如一个说话,不限定变量的取值,称为无类型说话(untyped language),我们既可以说它不具有类型,也可以说它具有一个通用类型,这个类型的取值范围是法度榜样中所有可能的值。

类型系统是类型化说话的一个组成部分,它用来谋略和跟踪法度榜样中所有表达式的类型,从而判断某段法度榜样是否体现优越(well behaved)。

假如法度榜样说话的语法中含有类型标记,就称该说话是显式类型化的(explicitly typed),否则就称为隐式类型化的(implicitly typed)。

像C、C++、Java等说话,都是显式类型化的。而像ML、Haskell、Groovy等可以省略类型声明,它们的类型系统会自动揣摸出法度榜样的类型。

4.2 编译时类型与运行时类型

Koltin是一门强类型的、静态类型、支持隐式类型的显式类型说话。

4.2.1 弱类型(Weakly checked language)与强类型(Strongly checked language)

类型系统最主要的感化是,经由过程反省类型的运算和转换历程,来削减类型差错的发生。假如一个说话的编译器引入越多的类型反省的限定,就可以称这个说话的类型反省越强,反之越弱。根据类型反省的强弱,我们把编程说话分为

弱类型说话

强类型说话

弱类型说话在运行时会隐式做数据类型转换。

强类型说话在运行时会确保不会发生未经明确转换(显式调用)的类型转换。

然则另一方面,强和弱只是相对的。

Kotlin是强类型说话。

4.2.2 静态类型(Statically checked language)与动态类型(Dynamically

checked language)

类型反省可发生在编译时期(静态反省)或运行时期(动态反省)。这样我们将编程说话分为

静态类型说话

动态类型说话

静态类型反省是基于编译器来阐发源码本身来确保类型安然。静态类型反省能让很多bug在编码早期被捕捉到,并且它也能优化运行。由于假如编译器在编译时已经证实法度榜样是类型安然的,就不用在运行时进行动态的类型反省,编译过后的代码会更优化,运行更快。

动态类型说话是在运行时期进行类型标记的反省,由于变量所约束的值,可经过运行路径得到不合的标记。关于动态类型,有个很形象的说法:

当看到一只鸟走起来像鸭子、泅水起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。——詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-1916)

Kotlin是静态类型说话。

4.2.3 显式类型(Explicitly typed language)与隐式类型(Implicitly typed language)

还有一种区分措施是,根据变量名是否必要显式给出类型的声明,来将说话分为

显式类型说话

隐式类型说话

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

大年夜多半静态类型说话,例如 Java、C/C++ 都是显式类型说话。然则有些则不是,如 Haskell、ML 等,它们可以基于变量的操作来揣摸其类型;

Scala 是静态类型说话,它应用类型揣摸功能来支持隐式类型。

Kotlin 跟Scala类似,它也应用类型揣摸支持隐式类型。然则,在一些场景下也必要显式声明变量的类型,以是我们可以说,同时也是显式类型。

4.3 根类型Any

Kotlin 中所有类都有一个合营的超类 Any ,假如类声明时没有指定超类,则默觉得 Any 。我们来看一段代码:

>>> val any = Any()

>>> any

java.lang.Object@2e377400

>>> 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 {

/**

* 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

}

4.3.1 工具相等性

从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() 函数。

4.4 基础类型(Primitive Types)

本节我们来探究进修:Kotlin的根基类型:数字、字符、布尔和数组等。

我们知道Java的类型分成两种:一种是基础类型,一种是引用类型。它们的本色差别是:

基础类型是在客栈惩罚配空间存“值”,而引用类型是在堆里面分配空间存“值”。

Java的基础类型有: byte、int、short、long、float、double、char、boolean,这些类都有对应的装箱类(引用类型)。

别的,void也可以算是一种特殊的基础类型,它也有一个装箱类Void(跟我们后文讲到的Unit、Nothing相关)。由于,Void是不能new出来的,也便是不能在堆里面分配空间存对应的值。以是,Void是一开始在客栈惩罚配好空间。以是,将Void归成基础类型。

在Kotlin中,统统皆是工具。所有类型都是引用类型。没有类似Java中的基础类型。然则,可以把Kotlin中对应的这几种基础数据类型,理解为Java的基础类型的装箱类。

Integer.java

public final class Integer extends Number implements Comparable {

/**

* A constant holding the minimum value an {@code int} can

* have, -231.

*/

@Native public static final intMIN_VALUE = 0x80000000;

/**

* A constant holding the maximum value an {@code int} can

* have, 231-1.

*/

@Native public static final intMAX_VALUE = 0x7fffffff;

/**

* The {@code Class} instance representing the primitive type

* {@code int}.

*

* @sinceJDK1.1

*/

@SuppressWarnings("unchecked")

public static final ClassTYPE = (Class) Class.getPrimitiveClass("int");

...

}

Kotlin中的Int类型:

public class Int private constructor() : Number(), Comparable {

companion object {

/**

* A constant holding the minimum value an instance of Int can have.

*/

public const val MIN_VALUE: Int = -2147483648

/**

* A constant holding the maximum value an instance of Int can have.

*/

public const val MAX_VALUE: Int = 2147483647

}

...

}

我们经由过程Java的Integer封装类,跟Kotlin的Int类的定义可以看出两者的思惟上的同源性。

Kotlin的基础类型的类图布局如下图所示

Kotlin极简教程

4.4.1 数字(Number)类型

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

类型

宽度(Bit)

Double

64

Float

32

Long

64

Int

32

Short

16

Byte

8

从上面的Kotlin的基础类型的类的布局图,我们可以看出这些内置的数据类型,都承袭了Number和 Comparable类。例如,Byte类型的声明:

public class Byte private constructor() : Number(), Comparable {

...

}

Kotlin 的数字类型跟 Java 基真相同。有一点不合的是,Kotlin 对付数字没有隐式拓宽转换(如 Java 中 int 可以隐式转换为long)。

留意在 Kotlin 中字符Char不是数字。这些基础数据类型,会在运行时自动优化为Java的double、float、long、int、short、byte。

字面常量值(literal constant values)

数值常量字面值有以下几种:

十进制: 123

Long 类型用大年夜写 L 标记: 123L

十六进制: 0x0F

二进制: 0b00001011

代码示例:

>>> 123

123

>>> 123::class

class kotlin.Int

>>> 123::class.java

int

>>> 123L

123

>>> 123L::class

class kotlin.Long

>>> 123L::class.java

long

>>> val b:Byte=128

error: the integer literal does not conform to the expected type Byte

val b:Byte=128

^

>>> val b:Byte=127

>>> b::class

class kotlin.Byte

>>> b::class.java

byte

>>> 0x0f

15

>>> 0x0F

15

>>> 0b1000

8

同样的,当我们赋值跨越变量的类型的取值范围时,编译器会直接抛错。

留意: 不支持八进制

Kotlin 同样支持浮点数的老例表示措施:

默认 double:123.5、123.5e10

Float 用 f 或者 F 标记: 123.5f

代码示例:

>>> 1234.5

1234.5

>>> 1234.5::class

class kotlin.Double

>>> 1234.5::class.java

double

>>> 12.3e10

1.23E11

>>> 12.3e10::class

class kotlin.Double

>>> 456.7f

456.7

>>> 456.7f::class

class kotlin.Float

>>> 456.7f::class.java

float

我们也可以应用数字字面值中的下划线(自 1.1 起),使数字常量更易读:

>>> 1_000_000

1000000

>>> 1234_5678_9012_3456L

1234567890123456

>>> 0xFF_EC_DE_5E

4293713502

>>> 0b11010010_01101001_10010100_10010010

3530134674

在 Java 平台数字是物理存储为 JVM 的原生类型,除非我们必要一个可空的引用(如 Int?)或泛型。

后者环境下会把数字装箱。

显式转换

因为不合的表示要领,值范围较小类型并不是较大年夜类型的子类型,是不能隐式转换的。

代码示例:

>>> val a: Int? = 1

>>> val b: Long? = a

error: type mismatch: inferred type is Int? but Long? was expected

val b: Long? = a

^

>>> val b: Byte = 1

>>> val i: Int = b

error: type mismatch: inferred type is Byte but Int was expected

val i: Int = b

^

这意味着在不进行显式转换的环境下我们不能把 Int 型值赋给一个 Long 变量。也不能把 Byte 型值赋给一个 Int 变量。

我们可以显式转换来拓宽数字

>>> val i: Int = b.toInt() // OK: 显式拓宽

每个数字类型都承袭Number抽象类,此中定义了如下的转换函数:

toDouble(): Double

toFloat(): Float

toLong(): Long

toInt(): Int

toChar(): Char

toShort(): Short

toByte(): Byte

以是,在数字之间的转换,我们直接调用上面的这些转换函数即可。

运算符+重载

短缺隐式类型转换并不显明,由于类型会从高低文揣摸出来,而算术运算会有重载做适当转换,例如:

val l = 1L + 3 // Long + Int => Long

这个是经由过程运算符+重载实现的。我们可以在Long类的源代码中看到这个plus 运算符函数的定义:

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

也便是说, 编译器会把1L + 3 翻译成 1L.plus(3),然后这个传入的参数类型必须是Byte、Short、Int、Long、Float、Double中的一种。例如,我们传入一个字符Char参数,编译器就会直接抛错:

>>> 'a'

a

>>> 'a'::class

class kotlin.Char

>>> 'a'::class.java

char

>>> 1L+'a'

error: none of the following functions can be called with the arguments supplied:

public final operator fun plus(other: Byte): Long defined in kotlin.Long

public final operator fun plus(other: Double): Double defined in kotlin.Long

public final operator fun plus(other: Float): Float defined in kotlin.Long

public final operator fun plus(other: Int): Long defined in kotlin.Long

public final operator fun plus(other: Long): Long defined in kotlin.Long

public final operator fun plus(other: Short): Long defined in kotlin.Long

1L+'a'

^

运算

Kotlin支持数字运算的标准集,运算被定义为响应的类成员(但编译器会将函数调用优化为响应的指令)。

对付位运算,没有特殊字符来表示,而只可用中缀要领调用命名函数(infix fun),例如:

val x = (1 shl 2) and 0x000FF000

这是完备的位运算列表(只用于 Int 和 Long):

shl(bits) – 有符号左移 (Java 的 )

shr(bits) – 有符号右移 (Java 的 >>)

ushr(bits) – 无符号右移 (Java 的 >>>)

and(bits) – 位与

or(bits) – 位或

xor(bits) – 位异或

inv() – 位非

4.4.2 Char: 字符(Character)类型与转义符(Escape character)

字符用 Char 类型表示。它们不能直接算作数字

fun check(c: Char) {

if (c == 1) { // 差错:类型不兼容

// ……

}

}

字符字面值用 单引号 括起来: '1'。

特殊字符可以用反斜杠转义。

Kotlin支持如下转义字符:

\t

\b

\n

\r

\`

\"

\\

\$

编码其他字符要用 Unicode 转义序列语法,例如:'\uFF00'。

Char类的函数接口定义如下:

public class Char private constructor() : Comparable {

/**

* Compares this value with the specified value for order.

* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,

* or a positive number if it's greater than other.

*/

public override fun compareTo(other: Char): Int

/** Adds the other Int value to this value resulting a Char. */

public operator fun plus(other: Int): Char

/** Subtracts the other Char value from this value resulting an Int. */

public operator fun minus(other: Char): Int

/** Subtracts the other Int value from this value resulting a Char. */

public operator fun minus(other: Int): Char

/** Increments this value. */

public operator fun inc(): Char

/** Decrements this value. */

public operator fun dec(): Char

/** Creates a range from this value to the specified [other] value. */

public operator fun rangeTo(other: Char): CharRange

/** Returns the value of this character as a `Byte`. */

public fun toByte(): Byte

/** Returns the value of this character as a `Char`. */

public fun toChar(): Char

/** Returns the value of this character as a `Short`. */

public fun toShort(): Short

/** Returns the value of this character as a `Int`. */

public fun toInt(): Int

/** Returns the value of this character as a `Long`. */

public fun toLong(): Long

/** Returns the value of this character as a `Float`. */

public fun toFloat(): Float

/** Returns the value of this character as a `Double`. */

public fun toDouble(): Double

}

我们来用代码示例这些函数的应用:

假如两个字符相等:

>>> 'a'.compareTo('a')

0

假如两个字符不相等:

>>> 'a'.compareTo('b')

-1

>>> 'a'.compareTo('c')

-1

>>> 'b'.compareTo('a')

1

>>> 'c'.compareTo('a')

1

Char字符只重载了加上Int类型的数字的+运算符:

>>> 'a'+1

b

>>> 'a'+1L

error: the integer literal does not conform to the expected type Int

'a'+1L

以是,当我们把一个Char类型值和不是Int类型的值相加,就报错了。

相减:

>>> 'a'-1

`

>>> 'c'-'a'

2

自增谋略:

>>> var a='a'

>>> val b=a++

>>> a

b

>>> b

a

>>> val c=++a

>>> c

c

我们不能在字符的字面量上直接应用++:

>>> 'a'++

error: variable expected

'a'++

^

>>> ++'a'

error: variable expected

++'a'

^

范围

>>> 'a'.rangeTo('z')

a..z

>>> for(c in 'a'..'z') {print(c)}

abcdefghijklmnopqrstuvwxyz

Char的显式类型转换函数如下:

/** Returns the value of this character as a `Byte`. */

public fun toByte(): Byte

/** Returns the value of this character as a `Char`. */

public fun toChar(): Char

/** Returns the value of this character as a `Short`. */

public fun toShort(): Short

/** Returns the value of this character as a `Int`. */

public fun toInt(): Int

/** Returns the value of this character as a `Long`. */

public fun toLong(): Long

/** Returns the value of this character as a `Float`. */

public fun toFloat(): Float

/** Returns the value of this character as a `Double`. */

public fun toDouble(): Double

例如,我们显式把字符转换为 Int 数字:

fun decimalDigitValue(c: Char): Int {

if (c !in '0'..'9')

throw IllegalArgumentException("Out of range")

return c.toInt() - '0'.toInt() // 显式转换为数字

}

测试代码:

>>> decimalDigitValue('a')

java.lang.IllegalArgumentException: Out of range

at Line24.decimalDigitValue(Unknown Source)

>>> decimalDigitValue('1')

1

4.4.3 Boolean: 布尔类型

Kotlin的布尔类型用 Boolean 类来表示,它有两个值:true 和 false。

>>> true::class

class kotlin.Boolean

>>> true::class.java

boolean

对应Java中的boolean类型。

其源码定义如下:

package kotlin

/**

* Represents a value which is either `true` or `false`. On the JVM, non-nullable values of this type are

* represented as values of the primitive type `boolean`.

*/

public class Boolean private constructor() : Comparable {

/**

* Returns the inverse of this boolean.

*/

public operator fun not(): Boolean

/**

* Performs a logical `and` operation between this Boolean and the [other] one.

*/

public infix fun and(other: Boolean): Boolean

/**

* Performs a logical `or` operation between this Boolean and the [other] one.

*/

public infix fun or(other: Boolean): Boolean

/**

* Performs a logical `xor` operation between this Boolean and the [other] one.

*/

public infix fun xor(other: Boolean): Boolean

public override fun compareTo(other: Boolean): Int

}

从上面我们可以看出,Boolean类的内置的布尔运算有:

!逻辑非 not()

&&短路逻辑与 and()

||短路逻辑或or()

xor 异或(相同false,不合true)

别的,Boolean还承袭实现了Comparable的compareTo()函数。

代码示例:

>>> !true

false

>>> true.not()

false

>>> true && true

true

>>> true.and(false)

false

>>> true || false

true

>>> false.or(false)

false

>>> true xor true

false

>>> true xor false

true

>>> false xor false

false

>>> true > false

true

>>> true >> true.compareTo(false)

1

>>> true.compareTo(false)

1

>>> true.compareTo(true)

0

>>> false.compareTo(true)

-1

4.4.4 String: 字符串类型

Kotlin的字符串用 String 类型表示。对应Java中的java.lang.String。字符串是弗成变的。

>>> "abc"::class

class kotlin.String

>>> "abc"::class.java

class java.lang.String

别的,在Kotlin中,String同样是final弗成承袭的。

代码示例:

>>> class MyString:String

error: this type is final, so it cannot be inherited from

class MyString:String

^

索引运算符 s[i]

字符串的元素——字符可以应用索引运算符 s[i]来造访。

>>> val s="abc"

>>> s

abc

>>> s[0]

a

当我们下标越界时,会抛越界差错:

>>> s[-1]

java.lang.StringIndexOutOfBoundsException: String index out of range: -1

at java.lang.String.charAt(String.java:646)

>>> s[3]

java.lang.StringIndexOutOfBoundsException: String index out of range: 3

at java.lang.String.charAt(String.java:646)

从掉足信息,我们可以看出,索引运算符 s[i]会被翻译成java.lang.String.charAt(), 背后调用的是Java的String类。其调用的措施是:

public char charAt(int index) {

if ((index = value.length)) {

throw new StringIndexOutOfBoundsException(index);

}

return value[index];

}

for 轮回迭代字符串

我们可以用 for 轮回迭代字符串:

>>> for(c in "abc") { println(c)}

a

b

c

关于字符串String类的完备的操作措施,我们可以看下源码:

public class String : Comparable, CharSequence {

companion object {}

/**

* Returns a string obtained by concatenating this string with the string representation of the given [other] object.

*/

public operator fun plus(other: Any?): String

public override val length: Int

public override fun get(index: Int): Char

public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence

public override fun compareTo(other: String): Int

}

类似的,字符串有一个length属性:

>>> "abc".length

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

>>> "abc"+arrayOf(1,2,3,4,5)

abc[Ljava.lang.Integer;@3d6f0054

截取字符串的子串:

>>> "abc".subSequence(0,1)

a

>>> "abc".subSequence(0,2)

ab

>>> "abc".subSequence(0,3)

abc

>>> "abc".subSequence(0,4)

java.lang.StringIndexOutOfBoundsException: String index out of range: 4

at java.lang.String.substring(String.java:1951)

at java.lang.String.subSequence(String.java:1991)

字符串字面值

字符串的字面值,可以包孕原生字符串可以包孕换行和随意率性文本,也可所以带有转义字符(Escape Charactor)的转义字符串。

>>> val s = "Hello,World!\n\n\n"

>>> s

Hello,World!

>>>

转义采纳传统的反斜杠要领。

原生字符串应用三个引号(""")分界符括起来,内部没有转义并且可以包孕换行和任何其他字符:

>>> val text = """

...for (c in "abc")

...print(c)

... """

>>> text

for (c in "foo")

print(c)

>>>

别的,在package kotlin.text下面的Indent.kt代码中,Kotlin还定义了String类的扩展函数:

fun String.trimMargin(marginPrefix: String = "|"): String

fun String.trimIndent(): String

我们可以应用trimMargin()、trimIndent() 裁剪函数往来交往除前导空格。可以看出,trimMargin()函数默认应用 "|" 来作为界限字符:

>>> val text = """

... |理论是你知道是这样,但它却不好用。

... |实践是它很好用,但你不知道是为什么。

... |法度榜样员将理论和实践结合到一路:

... |既不好用,也不知道是为什么。

..."""

>>> text.trimMargin()

理论是你知道是这样,但它却不好用。

实践是它很好用,但你不知道是为什么。

法度榜样员将理论和实践结合到一路:

既不好用,也不知道是为什么。

默认 | 用作界限前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")。

trimIndent()函数,则是把字符串行的左边空缺对齐切割:

>>> val text="""

...Hello

...World!

... """

>>> text.trimIndent()

Hello

World!

>>> val text="""

...Hello,

...World!

... """

>>> text.trimIndent()

Hello,

World!

字符串模板

字符串可以包孕模板表达式,即一些小段代码,会求值并把结果合并到字符串中。

模板表达式以美元符($)开首,由一个简单的名字构成:

>>> val h=100

>>> val str = "A hundred is $h"

>>> str

A hundred is 100

或者用花括号扩起来的随意率性表达式:

>>> val s = "abc"

>>> val str = "$s.length is ${s.length}"

>>> str

abc.length is 3

原生字符串和转义字符串内部都支持模板。

>>> val price=9.9

>>> val str="""Price is $$price"""

>>> str

Price is $9.9

>>> val str="Price is $$price"

>>> str

Price is $9.9

>>> val quantity=100

>>> val str="Quantity is $quantity"

>>> str

Quantity is 100

>>> val str="""Quantity is $quantity"""

>>> str

Quantity is 100

4.4.5 Array: 数组类型

数组在 Kotlin 中应用 Array 类来表示,它定义了 get 和 set 函数(映射到重载运算符 [])和 size 属性,以及一个用于变量数组的iterator()函数:

class Array private constructor() {

val size: Int

operator fun get(index: Int): T

operator fun set(index: Int, value: T): Unit

operator fun iterator(): Iterator

// ……

}

我们可以应用函数 arrayOf() 来创建一个数组并通报元素值给它。这个函数署名如下:

public inline funarrayOf(vararg elements: T): Array

此中,vararg表示是一个参数个数是一个变量。

例如, arrayOf(1, 2, 3) 创建了 array [1, 2, 3] :

>>> arrayOf(1,2,3)

[Ljava.lang.Integer;@4a37191a

>>> arrayOf(1,2,3)::class

class kotlin.Array

>>> arrayOf(1,2,3)::class.java

class [Ljava.lang.Integer;

别的,Kotlin还容许不合类型元素放到一个数组中,例如:

>>> val arr = arrayOf(1,"2",true)

>>> arr

[Ljava.lang.Object;@61af1510

>>> arr.forEach{ println(it) }

1

2

true

>>> arr.forEach{ println(it::class) }

class kotlin.Int

class kotlin.String

class kotlin.Boolean

Kotlin自动把这个数组元素的类型进级为java.lang.Object, 同时,因为Kotlin拥有的类型揣摸的功能,我们仍旧可以看到每个数组元素对应的各自的类型。

函数 arrayOfNulls() 可以用于创建一个指定大年夜小、元素都为空的数组。这个特殊的空数组在创建的时刻,我们必要指定元素的类型。假如不指定,直接按照下面这样写,会报错:

>>> arrayOfNulls(10)

error: type inference failed: Not enough information to infer parameter T in funarrayOfNulls(size: Int): Array

Please specify it explicitly.

arrayOfNulls(10)

^

也便是说,我们要指定

>>> arrayOfNulls(10)

[Ljava.lang.Integer;@77c10a5f

>>> arrayOfNulls(10).forEach{println(it)}

null

null

null

null

null

null

null

null

null

null

数组Array类,还供给了一个构造函数:

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

第1个参数是数组大年夜小,第2个参数是一个初始化函数类型的参数(关于函数类型,我们将在后面章节先容)。

代码示例:

>>> val square = Array(10, { i -> (i*i)})

>>> square

[Ljava.lang.Integer;@6f9e08d4

>>> square.forEach{ println(it) }

0

1

4

9

16

25

36

49

64

81

如上所述,[] 运算符代表调用成员函数 get() 和 set()。

代码示例:

>>> square[3]

9

>>> square[3]=1000

>>> square.forEach{ println(it) }

0

1

4

1000

16

25

36

49

64

81

与 Java 不合的是,Kotlin 中数组不是型变的(invariant)。 Kotlin中,我们不能把 Array 赋值给 Arrayhttps://github.com/EasyKotlin/chapter4_type_system

参考资料

https://jetbrains.github.io/kotlin-spec/#_the_kotlin_nothing_type

http://natpryce.com/articles/000818.html

您可能还会对下面的文章感兴趣: