Kotlin
和 Java
的基本类型对比Kotlin |
Java |
|
---|---|---|
字节 | Byte |
byte /Byte |
整型 | Int & Long |
int /Integer & long /Long |
浮点型 | Float & Double |
float /Float & double /Double |
字符 | Char |
char /Chararcter |
字符串 | String |
String |
val
只读变量 & var
可读写变量的定义定义 val
变量:
val foo: Type = initValue
定义 var
变量:
var foo: Type = initValue
其中:
关键字 val 表示变量 foo 为只读变量(相当于 Java 中被 final 修饰的变量)
关键字 var 表示变量 foo 为可读写变量
Type 表示变量类型
initValue 表示定义变量时的初始化赋值
举例:
// 相当于 Java 的 final String str = "Hello Kotlin";
val str: String = "Hello Kotlin"
// 相当于 Java 的 int a = 10;
var a: Int = 10
val foo = initValue
var foo = initValue
如上代码所示,在编译时期,Kotlin
可以根据初始化赋值自动推导变量的类型,所以定义变量时可以省略变量名后的 “: Type
”。
举例:
// 省略了 ": String",但是会根据初始值 "Hello Kotlin" 自动推导为 String 类型
var str = "Hello Kotlin"
// 省略了 ": Int" ,但是会根据初始值 5 自动推导为 Int 类型
var a = 10
注意:在
IntelliJ IEAD
中,使用快捷键Ctrl + Q
可以看到采用类型推导定义的变量的实际类型。
Kotlin
定义 Long
类型的变量:Java 中定义 long 类型变量:
long a1 = 12345678910l;
long a2 = 12345678910L;
如上所示,
Java
中可以在数值后面使用字母L
将该数值标记为long
类型(字母不区分大小写)
Kotlin 中定义 Long 类型变量:
val a = 12345678910L
如上所示,
Kotlin
中也可以在数值后面使用字母L
将该数值标记为long
类型(但是只能使用大写的字母L
)
Kotlin
定义浮点型的变量:val a = 1.0 // 变量a的类型会自动推导为 Double 类型
val b = 1.0f // 变量b的类型会自动推导为 Float 类型
Kotlin
中不支持 Int
类型到 Long
类型的隐式转换Java
代码:
int i = 10;
long l = i; // 隐式转换,编译通过
Kotlin
代码:
val i: Int = 10
val l: Long = i.toLong() // 不支持隐式转换,只能调用 toXxx 方法显示转换为指定类型
Kotlin
中不支持 Float
类型到 Double
类型的隐式转换val a: Double = 1.0f // 编译报错,应该改为 val c: Double = 1.0f.toDouble()
Kotlin
特有的无符号类型和 Java
不同的是,Kotlin
中存在无符号类型(这应该是为了 Kotlin-native
考虑的)
有符号类型 | 无符号类型 | |
---|---|---|
字节 | Byte |
UByte |
短整型 | Short |
UShort |
整型 | Int |
UInt |
长整型 | Long |
ULong |
字符串 | String |
String |
Kotlin
中的字符串${xxx}
)双引号字符串中可以使用 ${变量}
和 ${表达式}
, Kotlin
中称之为 字符串模板。
val hello = "Hello Kotlin"
val log = "---> ${hello}"
println(log) // ---> Hello Kotlin
val a = 2
val b = 3
val ret = "a + b = ${a + b}"
println(ret) // a + b = 5
==
& ===
)运算符 “==
” 比较 字符串内容 是否相同
运算符 “===
” 比较 字符串对象 是否是同一个(内存地址比较)
Raw String
)Kotlin
中可以使用 """..."""
包裹多行字符串
注意:调用
trimIndent()
函数 可以去掉定义多行字符串时的统一缩进
基本类型数组:Java
中的基本类型数组 t[]
对应 Kotlin
中的 TArray
类类型数组:Java
中的类类型数组 T[]
对应 Kotlin
中的 Array
Kotlin
和 Java
的数组对比Kotlin |
Java |
|
---|---|---|
整型 | IntArray |
int[] |
整型装箱 | Array |
Integer[] |
字符 | CharArray |
char[] |
字符装箱 | Array |
Character[] |
字符串 | Array |
String[] |
XxxArray
)xxxArrayOf(e1, e2, ...)
调用全局函数创建:xxxArrayOf(e1, e2, ...)
其中
xxx
是基本类型,如Char
对应char
,Int
对应int
,Float
对应float
。
返回基本类型数组
XxxArray
XxxArray(size){...}
调用构造方法 XxxArray(size)
创建指定元素个数的数组,元素值为默认值。
调用构造方法 XxxArray(size){初始化代码}
创建指定元素个数的,并对元素进行初始化。
初始化代码中可以使用默认参数
it
,表示元素索引Index
。
Array
)arrayOf(e1, e2, ...)
& arrayOfNulls(size)
arrayOf(ele1, ele2, ...)
,其中 Xxx
是类类型,返回类类型数组 Array
arrayOfNulls
,创建指定元素个数的数组,对象元素值为默认值 null
Array(size){...}
调用构造方法 Array
创建指定元素个数的,并对元素进行初始化。
初始化代码中可以使用默认参数
it
,表示元素索引Index
注意:
Kotlin
中使用size
属性表示数组长度(Java
中使用length
)
Kotlin
中调用contentToString()
函数打印数组和
Java
一样,Kotlin
中也使用下标运算符[]
访问数组元素
for-in
循环 & forEach
函数)Java
中可以使用 foreach
循环遍历数组
for(T ele : arr) {}
Kotlin
中:
可以使用 for-in
循环遍历数组
for(ele in arr) {} // 不需要显示指定元素类型
还可以使用 forEach
函数遍历数组
in
& !in
)判断元素是否存在:
if(ele in arr)
:元素 ele
存在则返回 true
if(ele !in arr)
:元素 ele
不存在则返回 true
其实就是在调用
contains
函数判断元素是否存在。
beginEle..endEle
)val range = beginEle..endEle // [beginEle, endEle]
注意:调用
joinToString()
函数可以打印区间
beginEle until endEle
)val range = beginEle until endEle // [beginEle, endEle)
endEle downTo beginEle
)val range = endEle downTo beginEle
step
)val range = beginEle..endEle step value // 步长为value
for-in
循环 & forEach
函数)语法:
val range = beginEle..endEle
// 1. for-in 循环遍历
for(ele in range) {}
// 2. forEach 函数遍历
range.forEach {
// 默认参数 it 表示当前遍历到的元素
}
in
& !in
)语法:
if(ele in range) {} // 元素 ele 存在,则为 true
if(ele !in range) {} // 元素 ele 不存在,则为 true
arr.indices
& 0 until arr.size
)语法:
for(i in arr.indices) {} // arr.indices 返回 IntRange 类型的区间, arr.indices 相当于 0 until arr.size
若 beginEle
和 endEle
是整型,则称为 离散区间。
若 beginEle
和 endEle
是浮点型,则称为 连续区间。
注意:
不能指定连续区间的步长
step
。不能遍历连续区间。
Kotlin
集合框架的特点Kotlin
增加了 不可变 集合框架的接口;
Kotlin
只是复用了 Java API
的集合类型,没有另外定义新的集合类型;
Kotlin
提供了大量的易于使用的函数,例如 forEach
/map
/flatMap
;
Kotlin为
集合框架提供了运算符级别的支持,简化了集合框架的使用。
Kotlin
和 Java
的集合框架接口对比Kotlin | Java | |
---|---|---|
不可变 List | List |
List |
可变 List | MutableList |
|
不可变 Map | Map |
Map |
可变 Map | MutableMap |
|
不可变 Set | Set |
Set |
可变 Set | MutableSet |
Kotlin
中的 可变 和 不可变 集合,编译时都会转化为 Java
中对应的集合。
Kotlin
中的不可变集合就是集合创建后,不能再修改集合。
Kotlin
中的部分集合类型只是 Java
集合类型的别名在 Koltin
中的部分集合类型,其实只是 Java
集合类型的一个别名而已,如下图:
在
Koltin
中,通过typealias
关键字为Java
集合类型起了个类名相同的别名,只是包名不同而已。
Koltin
这样做的目的是:为了让开发者使用Koltin
中定义的类型,而不要在Koltin
中使用Java API
,从而可以在除
JVM
平台之外的其他平台上运行Kotlin
代码。
List
集合List
集合List
集合(listOf
)val list = listOf(ele1, ele2, ele3, ...) // 不可变List集合没有提供 add 和 remove方法
List
集合(mutableListOf
)val list = mutableListOf(ele1, ele2, ele3, ...) // 可变的MutableList集合提供了 add 和 remove方法
ArrayList
集合val list = ArrayList() // 相当于Java中的 List list = new ArrayList<>();
注意:这里的
ArrayList
是kotlin.collections.ArrayList
,而不是Java
中的ArrayList
。
List
集合元素的增删(add
/+=
,remove
/-=
)List
集合中添加元素:
list.add(ele)
或
list += ele
List
集合中删除元素:
list.remove(ele)
或
list -= ele
List
集合元素(get
/set
/[]
)获取指定索引位置的元素:
val ele = list.get(index)
或
val ele = list[index]
修改指定索引位置的元素:
list.set(index, eleValue)
或
list[index] = eleValue
Map
集合Map
集合mapOf
)val map = mapOf(K1 to V1, K2 to V2, K3 to V3, ...) // K to V 表示创建一个键值对
val map= mutableMapOf(K1 to V1, K2 to V2, K3 to V3, ...)
HashMap
集合val map = HashMap() // 相当于Java中的 Map map = new HashMap<>()
这里的
HashMap
是kotlin.collections.HashMap
,而不是Java
中的HashMap
Kotlin
中的类型 Any
相当于 Java
中的类型 Object
)Map
集合put
/[]
)map.put(key, value)
或
map[key] = value
remove
)map.remove(key)
get
/[]
)val value = map.get(key)
或
val value = map[key]
Pair
Pair
的创建方式:key to value
/Pair(key, value)
val pair = key to value
或
val pair = Pair(key, value)
Pair
的访问方式:pair.first|second
/val (key, value) = pair
val key = pair.first
val value = pair.second
或:
val (key, value) = pair
注意:之所以支持这种操作方式,是因为 Pair 类中定义了运算符重载函数 component1 和 component2
component1
& component2
Triple
Triple
的创建方式:Triple(a, b, c)
val triple = Triple(a, b, c)
Triple
的访问方式:triple.first|second|third
/val (a, b, c) = tripleval a = triple.first
val b = triple.second
val c = triple.third
或
val (a, b, c) = triple
注意:之所以支持这种操作方式,是因为 Triple 类中定义了运算符重载函数 component1、component2、component3
component1
& component2
& component3
Kotlin
中,函数也是一种类型。
函数的定义语法:
fun funName(param1: Type1, param2: Type2, ...): ReturnType {
//函数体
}
其中,若返回值类型为 Unit(相当于 Java 中的 void),则可以省略,即:
fun funName(...): Unit {} <==> fun funName(...) {}
函数定义:fun funName(p1: T1, p2: T2, ...): ReturnType {}
对应函数类型:(T1, T2, ...) -> ReturnType
特别地:
无参函数:fun funName(): ReturnType {}
对应函数类型: () -> ReturnType
无返回值函数:fun funName(p1: T1, p2: T2, ...) {}
对应函数类型为:(T1, T2, ...) -> Unit
无参无返回值函数:fun funName() {}
对应函数类型为:() -> Unit
方法可以看成是一个特殊的函数,方法的函数类型需要指定其所属的类。
方法定义:
class Foo {
fun methodName(p1: T1, p2: T2, ...): ReturnType {}
}
对应函数类型:
Foo.(T1, T2, ...) -> ReturnType // 其中,Foo 称为方法的 Receiver
注意:
Java 中的非静态成员方法中有个隐式的 this,这个 this 就是方法的调用者,Java 内部是通过函数参数传到方法中的。
在 Kotlin 中,方法的 Receiver 对象就是 this,因为 this 是通过函数参数传入的,所以:
Foo.(T1, T2,...) -> ReturnType 还可以写成 (Foo, T1, T2,...) -> ReturnType
Foo.(T1, T2, ...) -> ReturnType
等价于
(Foo, T1, T2, ...) -> ReturnType
等价于
FuntionN // 其中 N 表示 <...> 中除去 ReturnType 之外的其他参数的总个数
如上是以成员方法的函数类型举例,但同样适用于全局函数的函数类型,即:
(T1, T2, ...) -> ReturnType
等价于
FuntionN
::
”在 c/c++
中,函数名就是一个指针常量,表示函数的入口地址,可以把函数名作为地址值赋给函数指针。
但是在 Kotlin
中,不能直接将函数名作为地址值直接赋给函数类型的变量,而是应该将 函数引用 赋给函数类型的变量。
Kotlin
中,通过运算符 “::
” 来获取函数引用。
::funName
)若有函数定义:
fun funName(p1: T1, p2: T2, ...): ReturnType {}
则函数引用为:
::funName
Foo::methodName
/foo::methodName
)获取成员方法的函数引用有两种方式:
通过类名来获取函数引用;
若有方法定义:
class Foo {
fun methodName(p1: T1, p2: T2, ...): ReturnType {}
}
则函数引用为:
Foo::methodName
通过对象来获取函数引用。
若有:
val foo = Foo()
则函数引用为:
foo::methodName
定义函数 funName
fun funName(p1: T1, p2: T2, ...): ReturnType {}
将函数引用 ::funName 赋给 (T1,T2,...) -> ReturnType 类型的变量 fun1
val fun1: (T1,T2,...) -> ReturnType = ::funName
因为根据函数引用 ::funName 可以推导出变量 fun1 的函数类型,所以可省略定义变量 fun1 时的类型声明
val fun1 = ::funName
定义方法 methodName
class Foo {
fun methodName(p1: T1, p2: T2, ...): ReturnType {}
}
将函数引用 Foo::methodName 赋给 (Foo,T1,T2,...) -> ReturnType 类型的变量 fun2
val fun2: (Foo,T1,T2,...) -> ReturnType = Foo::methodName
因为根据函数引用 Foo::methodNamee 可以推导出变量 fun2 的函数类型,所以可省略定义变量 fun2 时的类型声明
val fun2 = Foo::methodName
通过类名获取的函数引用时,因为函数引用 Foo::methodName 对应的函数类型是 (Foo, T1, T2,...) -> ReturnType
所以通过变量 fun2 调用方法 methodName 时,参数 1 必须传入一个 Foo 对象作为方法的 Receiver(也就是方法内部的 this)
通过对象获取的函数引用时,因为对于:
val foo = Foo()
val fun3 = foo::methodName
此时,变量 fun3 的函数类型是 (T1, T2, ...) -> ReturnType
从函数类型的形参列表中可以看出,通过变量 fun3 调用方法 methodName 时,不需要传入 Foo 类型的参数了。
()
/invoke
对于函数类型的变量 funRef:
val funRef: (T1, T2, T3, ...) -> ReturnType = ::funName
通过该变量调用函数有2种方式:
1. funRef(t1, t2, t3, ...)
2. funRef.invoke(t1, t2, t3, ...)
args: Array
/vararg args: String
定义方式 1:
fun funName(args: Array) {} // 相当于 Java 中的 (String[] args)
定义方式 2:
fun funName(vararg args: String) {} // 相当于 Java 中的 (String... args)
其中关键字
vararg
用来声明形参args
是一个可变参数。
main
函数的两种写法(无参数/可变参数)无参的 main
函数:
fun main() {}
带可变参数的 main
函数:
fun main(vararg args: String) {}
fun funName(p1: T1, p2: T2, ...): Pair {
...
return Pair(rt1, rt2)
}
// 调用
val (a, b) = funName(p1, p2, ...)
fun funName(p1: T1, p2: T2, ...): Triple {
...
return Triple(rt1, rt2, rt3)
}
// 调用
val (a, b, c) = funName(p1, p2, ...)
fun funName(p1: T1, p2: T2, ..., pm: Tm = defaultValue1, pn: Tn = defaultValue2, ...): ReturnType {}
注意:具体默认参数值的形参建议 声明在形参列表的最后面,否则调用函数时编译器推断不出实参所对应的形参是哪个。
当然,在调用时可以通过 显示指定形参名 来避免这种问题,如:
fun funName(p1: T1 = defvalue1, p2: T2, p3: T3 = defValue3): ReturnType {}
// 调用
funName(value2) // 编译报错,无法确定实参 value2 对应哪个形参
funName(p2 = value2) // 编译成功,通过显示指定形参名 p2,将实参 value2 赋给形参 p2