本文是对<<Kotlin in Action>>
的学习笔记,若是须要运行相应的代码能够访问在线环境 try.kotlinlang.org,这部分的思惟导图为: java
Java
把基本数据类型和引用类型作了区分:数组
int
的变量直接存储了它的值,咱们不能对这些值调用方法,或者把它们放到集合中。Kotlin
不区分基本数据类型和引用类型,它使用的永远是一个类型(例如Int
),此外,你还能对一个数字类型的值调用方法。ide
在运行时,数字类型会尽量地使用最高效的方式来表示,大多数状况下,对于变量、属性、参数和返回类型,Kotlin
的Int
类型会被编译成Java
基本数据类型int
。惟一不可行的例外是泛型类,例如集合,用做泛型类型参数的基本数据类型会被编译成对象的Java
包类型。函数
对应到Java
基本数据类型的类型完整列表以下:学习
Byte
、Short
、Int
、Long
Float
、Double
Char
Boolean
像Int
这样的Kotlin
类型在底层能够轻易地编译成对应的Java
基本数据类型。而在Kotlin
中使用Java
声明时,Java
基本数据类型会变成非空类型,由于它们不能持有null
值。spa
Kotlin
中的可空类型不能用Java
的基本数据类型表示,由于null
只能被存储在Java
的引用类型的变量中。任什么时候候,只要使用了基本数据类型的可空版本,它就会被编译成对应的包装类型,而且不能比较两个可空基本数据类型的大小,由于它们之中任何一个均可能为null
。设计
除此以外,泛型类是包装类型应用的另外一种状况,若是你 用基本数据类型做为泛型类的类型参数,那么 Kotlin 会使用该类型的包装形式,例以下面这段代码,就会建立一个Integer
包装类的列表,尽管你历来没有指定过可空类型或者用过null
值:3d
val listOfInts = listOf(1, 2, 3)
复制代码
这是由Java
虚拟机实现泛型的方式决定的,JVM
不支持用基本数据类型做为类型参数,因此泛型类必须始终使用类型的包装表示。code
Kotlin
和Java
之间一条重要的区别就是处理数字转换的方式,Kotlin
不会自动地把数字从一种类型转换成另外一种,即使是转换成范围更大的类型,咱们必须 显示地转换,对每一种基本数据类型都定义有转换函数:toByte()
、toShort()
、toChar()
等,这些函数支持双向转换: cdn
equals
不只会检查它们存储的值,还要比较装箱类型,也就是说
new Integer(42).equals(new Long(42))
会返回
false
。
Kotlin
除了支持简单的十进制数字以外,还支持下面这些在代码中书签数字字面值的方式:
L
表示Long
:123L
Double
:0.12
、1.2e10
和1.2e-10
。F
表示Float
:123.4f
、.456F
和1e3f
。0x
或者0X
表示十六进制:0xbcdL
。0b
或者0B
表示二进制字面值:0b0001
。当你使用数字字面值去初始化一个类型已知的变量时,又或是把字面值做为实参传给函数时,必要的转换会自动地发生。
Any
类型是Kotlin
全部非空类型的超类型,包括像Int
这样的基本数据类型,和Java
同样,把基本数据类型的值赋给Any
类型的变量会自动装箱。
在Kotlin
中,若是你须要能够持有任何可能值的变量,包括null
在内,必须使用Any?
类型。
在底层,Any
类型对应java.lang.Object
,Kotlin
把Java
方法参数和返回类型中用到的Object
类型看做Any
,当Kotlin
函数函数中使用Any
时,它会被编译成Java
字节码中的Object
。
全部的Kotlin
类都包含下面三个方法:toString
、equals
和hashCode
,这些方法都继承自Any
。Any
不能使用其它Object
的方法(例如wait
和notify
),可是能够经过手动把值转换成java.lang.Object
来调用这些方法。
Kotlin
中的Unit
类型完成了Java
中的void
同样的功能,当函数没有有意思的结果要返回时,它能够用做函数的返回类型:
fun f() : Unit { .. }
复制代码
Unit
是一个完备的类型,能够做为类型参数,而void
却不行。只存在一个值是Unit
类型,这个值也叫作Unit
,而且(在函数中)会被隐式地返回,当你在重写返回泛型参数的函数时这很是有用,只须要让方法返回Unit
类型的值:
对于某些Kotlin
函数来讲,“返回类型”的概念没有任何意义,由于它们历来不会成功地结束,Kotlin
使用一种特殊的返回类型Nothing
来表示:
Nothing
类型没有任何值,只有被看成函数返回值使用,或者被看成泛型函数返回值的类型参数使用才会有意义,在其它状况下,声明一个不能存储任何值的变量没有任何意义。
返回Nothing
的函数能够放在Elvis
运算符的右边来作先决条件检查:
在 Kotlin 知识梳理(6) - Kotlin 的可空性 中,咱们讨论了可空类型的概念,但仅仅简略地谈到类型参数的可空性,其实集合也能够持有null
元素,和变量能够持有null
同样,类型在被看成类型参数时也能够用一样的方式来标记。
下面咱们建立一个包含可空值的集合,以后遍历该集合,打印出有效的数字之和以及为null
的集合元素个数:
Kotlin
的集合设计与Java
不一样的另外一项重要特质是:它把访问集合数据的接口和修改集合数据的结构分开了:
kotlin.collections.Collection
:使用这个接口,能够遍历集合中的元素、获取集合大小、判断集合中是否包含某个元素,执行其余从该集合中读取数据的操做。kotlin.collections.MutableCollection
:修改集合中的数据。通常的原则是:在代码的任何地方都应该使用只读接口,只在代码须要修改集合的地方使用可变接口的变体。
下面的例子演示了如何使用只读集合和可变集合:
每个Kotlin
接口都是其对应Java
集合接口的一个实例,在Kotlin
和Java
之间转移并不须要转换;不须要包装器也不须要拷贝数据。
每一种Java
集合接口在Kotlin
中都有两种表示:一种是只读的,另外一种是可变的。在下图当中,能够看出Kotlin
集合接口的层级结构,Java
类ArrayList
和HashSet
都继承了Kotlin
可变接口。
Kotlin
中只读接口和可变接口的基本结构与
java.util
中的
Java
集合接口的结构是平行的。可变接口直接对应
java.util
包中的接口,而它们的只读版本缺乏了全部产生改变的方法。
上图中包含了Java
类中的ArrayList
和HashSet
,在Kotlin
看来,它们分别继承自MutableList
和MutableSet
接口,这样既获得了兼容性,也获得了可变接口和只读接口之间清晰的分离。
除了集合以外,Kotlin
中Map
类也被表示成了两种不一样的版本:Map
和MutableMap
。咱们以前见到的listOf/setOf/mapOf
所返回的都是只读版本。
当你有一个使用java.util.Collection
作形参的Java
方法,能够把任意Collection
或MutableCollection
的值做为实参传递给这个形参。Java
并不会区分只读集合和可变集合,也就是说即便Kotlin
中把集合声明成只读的,Java
代码也能够修改这个集合,例以下面的代码,虽然咱们将printInUppercase
接收的list
参数声明为只读的,可是仍然能够经过Java
代码修改它。
//CollectionUtils.java
public class CollectionUtils {
public static List<String> uppercaseAll(List<String> items) {
for (int i = 0; i < items.size(); i++) {
items.set(i, items.get(i).toUpperCase());
}
return items;
}
}
//collections.kt
fun printInUppercase(list : List<String>) {
println(CollectionUtils.uppercaseAll(list));
println(list.first())
}
复制代码
前面咱们介绍过,Kotlin
把那些定义在Java
代码中的类型当作 平台类型,Kotlin
没有任何关于平台类型的可空性信息,因此编译器容许Kotlin
代码将其视为可空或者非空,一样,Java
中声明的集合类型的变量也被视为平台类型。
当咱们须要重写或者实现签名中有集合类型的Java
方法时,这些差别才变得重要,咱们须要决定使用哪种Kotlin
类型来表示这个Java
类型,它们会反映在产生的Kotlin
参数类型中:
例以下面这个使用集合参数的Java
接口:
interface DataParser<T> {
void parseData(String input, List<T> output, List<String> errors);
}
复制代码
咱们的选择为:
List<String>
将是非空的,由于调用者老是须要接收错误信息。List<String>
将是可变的,由于实现代码须要向其中添加元素。那么Kotlin
的实现以下:
class PersonParser : DataParser<Person> {
override fun parseData(input : String, output : MutableList<Person>,
errors : MutableList<String?>)
}
复制代码
Kotlin
中的一个数组是一个带有类型参数的类,其元素类型被指定为相应的类型参数,要在Kotlin
中建立数组,有下面这些方法供你选择:
arrayOf
函数建立一个数组,它包含的元素是指定为该函数的实参arrayOfNulls
建立一个给定大小的数组,包含的是null
元素,固然,它只能用来建立包含元素类型可空的数组Array
构造方法接收数组的大小和一个lambda
表达式,调用lambda
表达式来建立每个数组元素,这就是使用非空元素类型来初始化数组,但不用显示地传递每一个元素的方式数组类型的类型参数始终会变成对象类型,所以,若是你声明了一个Array<Int>
,它将会是一个包含装箱整型的数组,若是你须要建立没有装箱的基本数据类型的数组,必须使用一个基本数据类型数组的特殊类。
Kotlin
提供了若干个独立的类,每一种基本数据类型对应一个,例如Int
类型值的数组叫做IntArray
,要建立一个基本数据类型的数组,有以下的选择:
size
参数并返回一个使用对应基本数据类型默认值初始化好的数组。IntArray
的intArrayOf
,以及其余数组类型的函数)接收变长参数的值并建立和存储这些值的数组。lambda
。