基本数据类型
在Kotlin中所有东西都是对象,包括数字,布尔,字符串等,但是它们在运行时可以表示为原生类型值。
数字
Kotlin中的数字没有隐式拓宽。Double参数的值只能对Double值调用,不能对Float,Int调用。如果想要将数值转化为不同的类型,需要使用显示转换。
整数类型
类型 | 大小 (字节) | bit |
---|---|---|
Byte | 1 | 8 |
Short | 2 | 16 |
Int | 4 | 32 |
Long | 8 | 64 |
Tips:未超出Int最大值的整型会被推断为Int,超过的则会被推断为Long。显示指定Long类型值,可以在值后面追加L。
val b:Byte=12
val s:Short=20
val i=1000
val l=200003203203203202
val lL=20L
val c:Double=b.toDouble()
println(b.javaClass.simpleName)//byte
println(s.javaClass.simpleName)//short
println(lL.javaClass.simpleName)//long
println(c.javaClass.simpleName)//double
浮点类型
类型 | 大小(字节) | bit | 十进制位数 |
---|---|---|---|
Float | 4 | 32 | 6~7 |
Double | 8 | 64 | 15~16 |
Tips:
1.对于浮点类型,编译器默认推断为Double类型。
2.显示指定为Float类型,可以在值后面添加f后者F。
3.如果显示指定为Float类型,其十进制位数超过6~7,那么其后面多余的位数将被舍弃。
字面常量
Koltin不支持八进制。
十进制:123, Long类型:123L
十六进制:0x0F
二进制:0b00001011
浮点数表示:
默认Double:123.5,123.5e10
Float:123.5f,123.5F。
添加下划线(_)是数字更加易读
val a=1_000_000
val b=0x_12_AC_BC
Jvm平台的数字表示
Jvm平台数字存储为原生类型int,double等类型。
Int?(可空类型)或使用泛型,在这些场景下数字会自动装箱为Integer,Double类型。
val a=20
val b:Int?=a
val c=130
val d:Int?=c
//引用比较
println(a===b)//true
println(c===d)//false
//值比较
println(a==b)//true
println(c==d)//true
Jvm会对-128到127的整数进行内存优化,所以a,b其实指向的是同一个对象。c,d因为已经超出了127所以没有优化,是不同的对象。
显示转换
较小的类型并不是较大类型的子类,所以把一个类型赋值给另一个类型需要显示转换。
val a:Int=20
val b:Long=a //报错
val c:Long=a.toLong()//显示转换
自动推断
有些时候不需要显示转换,类型会从上下文中推断出来,算术运算符会有重载做适当转换。
val a:Int=20
val b=10L+a
println(b is Long) //true
运算
Koltin支持标准的运算符集:+,-,*,,%,并支持运算符重载。
整数除法
整数间的除法总是返回整数,会丢弃小数部分。如果需要返回浮点类型,可以将其中任何一个显示转换为浮点类型。
位运算
只针对整数类型,可以通过中缀形式调用的函数表示,只能作用于Int,Long。
运算 | 说明 |
---|---|
shl(bits) | 有符号做移 |
shr(bits) | 有符号右移 |
ushr(bits) | 无符号右移 |
and(bits) | 与 |
or(bits) | 或 |
xor(bits) | 异或 |
inv() | 非 |
Q:为什么Java和Kotlin中没有无符号左移?
A:因为左移是在后面补0,而右移是在前面补1或0,有无符号是取决于数的前面第一位是1还是0,所以右移会产生在前面补1或0的问题,而左移始终是往后面补0,不存在符号的问题,所以没有必要无符号左移。
val b:Byte=1
val s:Short=2
val i: Int = 3
val l:Long=4L
val s1=b shl 2 // 报错
val s2=s shl 2 // 报错
val s3=i shl 2
val s4=l shl 2
浮点数比较
相等性检测:a==b,a!=b
比较操作符:a>b,a<b,a>=b,a<=b
区间实例以及区间检测:a…b,x in a…b,x !in a…b
无符号整型
类型 | 大小(字节) | 范围 |
---|---|---|
UByte | 1 | 0到255 |
UShort | 2 | 0 到65535 |
UInt | 4 | 0 到 2^32 - 1 |
ULong | 8 | 0 到 2^64 - 1 |
UByteArray:无符号字节数组
UShortArray:无符号短整型数组
UIntArray:无符号整型数组
ULongArray:无符号长整型数组
字面值
u与U将字面值标记为无符号。
uL与UL将字面值标记为无符号长整型。
布尔
Kotlin中布尔位Boolean类型,可定义为可空类型Boolean?。
字符
用Char表示,字符字面值用单引号括起。
特殊字符可以使用转义反斜杠\。
编码其它字符需要使用Unicode转义序列语法:‘\uFF00’
val chr:Char='a'
val unicode:Char='\uFF00'
字符串
用String表示,是不可变的。可以使用索引来访问字符串的元素-字符。
val str = "Hello World"
val firstChar=str[0]
val lastChar=str[str.lastIndex]
println(firstChar) // H
println(lastChar) // d
字符串链接可以使用:+操作符,也用于连接字符串与其它类型数据,只要表达式中的第一个元素是字符串即可。
Tips;大多数情况下推荐使用字符串模版或者原始字符串。
val h = "Hello"
val w="World"
println(h+w) //"HelloWorld"
val a:Int =2
println(h+a) // "Hello2
字符串字面值
转义字符串:可以包含转义字符。
原始字符串:可以包含换行及任意文本。
val str = "Hello \n World"
val str1 = """|Hello
|World
|str1
""".trimMargin()//默认使用|作为边界前缀,可自定义,例如:用">",.trimMargin(">")。
println(str)
println(str1)
输出结果:
Hello
World
Hello
World
str1
字符串模版
字符串字⾯值可以包含模板表达式——⼀些⼩段代码,会求值并把结果合并到字符串
中。 模板表达式以美元符( $ )开头。用两种形式:$a, ${a}
val a=20
val b=20
val str = "Hello World $a"
val str1 = "a+b=${a+b}"
println(str) // Hello World 20
println(str1) // a+b=40
数组
用Array表示,定义了get与set函数(按照重载运算符约定会变为[])以及size属性和其它有用函数。
Kotlin中的数组是不型变的,所以Array<String>并不能赋值给Array<Any>。
创建
非原生类型数组,会有装箱开销。
arrayOf(vararg elements:T)
arrayOfNulls(size:Int)
Array(size: Int, init: (Int) -> T)
val arr = arrayOf(1, 2, 3, 4, 5, 6)
val arr1 = arrayOfNulls<Int>(5)
val arr2 = Array(5) {
it*2
}
println(arr.contentToString())
println(arr1.contentToString())
println(arr2.contentToString())
输出结果:
[1, 2, 3, 4, 5, 6]
[null, null, null, null, null]
[0, 2, 4, 6, 8]
原生类型数组,无装箱开销
ByteArray,ShortArray,IntArray等等,这些类与Array并没有继承关系。
构建方法:
byteArrayOf,shortArrayOf,intArrayOf等等工厂方法来提供构建能力。
IntArray(size: Int)
IntArray(size: Int, init: (Int) -> Int)
//非原生数组类型Array与原生类型数组IntArray对比
val arr= arrayOf(1,2,3)
val arr1= intArrayOf(1,2,3)
反编译后的Java代码
Integer[] var10000 = new Integer[]{1, 2, 3};
int[] var2 = new int[]{1, 2, 3};
类型检测与类型转换
is,!is在运行时可以检测对象是否是指令类型。
智能转换
大多数情况下,在使用is操作符以后,会自动把对象转换为目标类型。
请注意,当编译器能保证变量在检测和使⽤之间不可改变时,智能转换才有效。 更具体
地,智能转换适⽤于以下情形:
val 局部变量——总是可以,局部委托属性除外。
val 属性——如果属性是 private 或 internal,或者该检测在声明属性的同⼀模块
中执⾏。智能转换不能⽤于 open 的属性或者具有⾃定义 getter 的属性。
var 局部变量——如果变量在检测和使⽤之间没有修改、没有在会修改它的
lambda 中捕获、并且不是局部委托属性。
var 属性——决不可能(因为该变量可以随时被其他代码修改)。
转换操作符 as 不安全的
转换失败会跑出异常。
Tips:null不能转换为不可空类型,必须说可空类型才可以。
转换操作符 as? 安全的
转换失败不会抛出异常,会返回null。
类型擦除与泛型检测 <待补充>
泛型类型擦除后编译器禁止运行时使用is类型检测,但是可以对非泛型部分检测。