写在开头:前段时间阅读KT重构后的LeakCanary源码,发现对KT的许多知识点有所遗忘,决定再次从头开始系统学习KT,并以文章记录,使自己记忆和理解的更加深刻
基础数据类型
| 基本类型名称 | kotlin数据类型 | Java数据类型 | 转换为该类型 |
|---|---|---|---|
| 整型 | Int | int和Integer | toInt |
| 长整型 | Long | long和Long | toLong |
| 浮点型 | Float | float和Float | toFloat |
| 双精度 | Double | double和Double | toDouble |
| 布尔型 | Boolean | boolean和Boolean | 无 |
| 字符型 | Char | char | toChar |
| 字符串 | String | String | toString |
基本数据类型包括如:
- 数值类型:整形,长整形,浮点型,双精度
- boolean
- 字符型
- 字符串
Java语言中有两种数据类型:
- 基本类型,如int,double等
- 引用类型,String等
而在kotlin中,只有一种类型,就是引用类型
数值类型
数值比较
Kotlin 中没有基础数据类型,只有封装的数字类型,你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。
类型转换
// 不能直接像Java一样,可以数值类型隐式转换(Int转Long),而是需要调用方法显式转换
val e:Int = 10
val f:Long = e.toLong()
boolean
布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。
内置的布尔运算有:
|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非
字符串
和 Java 一样,String 是不可变的。方括号 [] 语法可以很方便的获取字符串中的某个字符,也可以通过 for 循环来遍历
字符
字符用单引号括起来: '1'。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t、 \b、\n、\r、'、"、\ 和 $。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'。
| 转义序列 | 描述 |
|---|---|
| \b | 退格符(Backspace) |
| \t | 水平制表符(相当于tab,缩进) |
| \n | 换行符 |
| \f | 换页符 |
| \r | 回车符 |
| " | 转义" |
| \ | 转义\ |
回车 \r 本义是光标重新回到本行开头。r 的英文return,控制字符可以写成CR,即Carriage Return
换行 \n 本义是光标往下一行(不一定到下一行行首)。n 的英文newline,控制字符可以写成LF,即Line Feed
声明变量
| Kotlin | Java | |
|---|---|---|
| var | var testVar : Int = 1 | private int testVar = 1; |
| val | var testVal : Int = 1 | private final int testVal = 1; |
| const | const val testConst = 1 | public static final int testConst = 1; |
数组
| 基本数组类型 | 名称 | 初始化 |
|---|---|---|
| 整型数组 | IntArray | intArrayOf |
| 长整型数组 | LongArray | longArrayOf |
| 浮点数组 | FloatArray | floatArrayOf |
| 双精度数组 | DoubleArray | doubleArrayOf |
| 布尔型数组 | BooleanArray | booleanArrayOf |
| 字符数组 | CharArray | charArrayOf |
数组的创建
Kotlin中并没有找到StringArray的数组类型,是因为String是一种特殊的基本数据类型(可以理解为它是一个对象),所以如果想要创建的话需要使用Array<>类型,也就是把String用<>扩起来,同时分配字符串的方法也变成了arrayOf,以下是创建数组的两种方式
var stringArrayTest: Array<String> = arrayOf("hello", "world")
val c1: IntArray = IntArray(5) {
it + 1
}
数组的长度
java使用的是.length,kotlin使用的是.size
val c0: IntArray = intArrayOf(1, 2, 3, 4)
println(c0.size)
数组的读写
java使用的是方括号加下标的方式来获取,比如int_array[0],kotlin也可以通过此方式来获取,kotlin也有自己的获取方式,get/set,通过get获取值,set设置值。
var stringArrayTest: Array<String> = arrayOf("hello", "world")
stringArrayTest[0]
stringArrayTest[0] = "1"
stringArrayTest.get(0)
stringArrayTest.set(0,"1")
数组的遍历
val ints = intArrayOf(1, 2, 3, 4)
// 使用for循环
for (num in ints) {
println(num)
}
// 使用forEach
ints.forEach { num ->
println(num)
}
for (i in 0 until arr2.size) {
println(arr2[i])
}
for (i in arr2.indices) {
println(arr2[i])
}
判断值是否在数组中
val ints = intArrayOf(1, 2, 3, 4)
if (1 in ints) {
println("num is in ints")
}
if (5 !in ints) {
println("num is not in ints")
}
区间
区间的创建
// 闭区间;范围在[1,10]
val intRange1: IntRange = 1..10
// 前闭后开区间;范围在[1,10)
val intRange2: IntRange = 1 until 10
// 倒序区间;范围在[10,1]
val intRange3: IntProgression = 10 downTo 1
区间的步长
val intRange1: IntProgression = 1..10 step 2
离散值区间与连续值区间
// 离散值,可以一个一个取值,可数可遍历,有步长step
val intRange: IntRange = 1..10
println(intRange.joinToString())
// 连续值,不可以一个一个取值,不可数不可遍历,没有步长step
val floatRange = 1f..2f
val doubleRange = 1.0..2.0
// 没有 joinToString 方法,只能 toString
println(floatRange.toString())
println(doubleRange.toString())
区间的迭代
// 只有离散区间才可以迭代遍历
val intRange: IntRange = 1..10
for (num in intRange) {
println(num)
}
intRange.forEach { num ->
println(num)
}
区间的包含关系
不论区间是离散区间,还是连续区间,都可以使用 in 来判断包含关系;
val intRange: IntRange = 1..10
if (3 in intRange) {
println("num is in intRange")
}
if (12 in intRange) {
println("num is not in intRange")
}
区间的使用
val ints:IntArray = intArrayOf(1, 2, 3, 4)
// 可以使用区间来代替 i++ 的循环
for (i in 0 until ints.size) {
println(ints[i])
}
// ints.indices 等价于 0 until ints.size
// Kotlin 中推荐使用 ints.indices
for (i in ints.indices) {
println(ints[i])
}
集合
集合分为三种类型:List,Map,Set,这与Java一致,与Java不一致的是,kotlin拥有可变集合与不可变集合
不可变集合
使用List<>,Map<>,set<>方法创建的集合是不可变集合
var list1: List<Int> = arrayListOf(1, 2, 3)
var map1: Map<Int, Int> = mapOf(1 to 1, 2 to 2)
var set1: Set<Int> = setOf(1, 2)
可变集合
使用mutableListOf<>,mutableMapOf<>,mutableSetOf<>方法创建的集合是可变集合,提供了add,remove方法,使用+=,-=操作符进行重载
var list2: List<Int> = mutableListOf(1, 2)
var map2: Map<Int, Int> = mutableMapOf(1 to 2, 2 to 2)
var set2: Set<Int> = mutableSetOf(1, 2)
list2 += 3
list2 -= 3
不可变集合的可变性
这里,我们在Java中创建了一个集合,在kotlin中进行引用,由于Java中的集合并不区分可变集合与不可变集合,所以在kotlin中引用时,可同时存在可变与不可变两个引用,在修改可变引用后,不可变引用也被修改
public static List<String> stringList = new ArrayList<String>(Arrays.asList("1","2","3"));
var list1 : List<String> = TestJava.stringList
var list2 : MutableList<String> = TestJava.stringList
list2.add("4")
逆变与逆变
在这里,我们新建了一个person类,work1和work2继承至person
open class person (var name : String, var age : Int){
open fun toWork(){
println("我是工人${name},今年${age}岁")
}
}
class work1(var name1: String, var age1: Int) : person(name1, age1) {
override fun toWork() {
println("我是工人1${name},今年${age}岁")
}
}
class work2(var name2: String, var age2: Int) : person(name2, age2) {
override fun toWork() {
println("我是工人2${name},今年${age}岁")
}
}
fun main() {
val personArrayList: MutableList<person> = mutableListOf(person("aaa", 11))
val personArrayList1: MutableList<work1> = mutableListOf(work1("ddd", 14))
val personArrayList2: MutableList<work2> = mutableListOf(work2("eee", 15))
setWork(personArrayList)
setWork(personArrayList1)
setWork(personArrayList2)
}
协变
加上out,out表示,以person为上界,传入进来的都是person的子类,因此协变获取的列表是只能get,不能add,因为无法确定传进来的参数是什么类型的,无法将一个子类赋值给一个父类
fun setWork(studentList: MutableList<out person>) {
studentList.get(1)
}
逆变
加上in,in表示,以work1为上界,传入进来的都是work1的父类,所以逆变获取的列表是可以get也可以add的,因为传进来的都是work1的父类,可以将一个父类赋值给一个子类
fun setWork(studentList: MutableList<in work1>) {
studentList.get(1)
studentList.add(work1("1",1))
}
条件控制
if else
Kotlin中的if语句与Java中的if语句用法相似,但kotlin中的if表达式的结果可以赋给一个值
var ifTest = if (1 > 2) {
println("这是1")
1
} else {
println("这是2")
2
}
所以kotlin中并不需要类似Java中那种三元运算符,可以直接使用这种方式实现:
var ifTest = if (1 > 2) 1 else 2
when
相比于Java,Kotlin的分支可以是任何类型,else对应Switch中的default,同时,与if一致,when表达式的结果同样也可以赋给一个值
val x : Any? = null
var whenTest = when (x) {
1, 2 -> print("这是1")
"x" -> print("这是X")
true -> print("这是true")
is String -> print("这是字符串")
else -> {
print("啥也不是")
"啥也不是"}
}
字符串模板
这里${}能够引用的变量类型不仅仅适用于String,可适用其他的基本数据类型。除此之外,在它的里面还可以调用函数,通过函数的返回值进行输出
val time = 6
val boolean = true
println("今天去${if (boolean) "去这玩" else "去那玩"} 玩了$time 分钟")
// 支持 RawString;使用三个双引号
var content:String = """
Hello
Kotlin
基本类型
"""
函数
函数的定义
- 以 fun 关键字开头
- 返回值写在了函数和参数后面
// 函数名getName,参数String,返回值Unit(相当于Java中的void)
fun getName(name:String):Unit
{
println("name-$name")
}
匿名函数
函数定义的时候需要给函数定义名称,以便该函数在其他地方通过类名+函数名进行调用,kotlin中可以定义的时候不指定函数名,这样的函数就是匿名函数;
没有函数名就不能正常的通过函数名进行调用执行,那就需要一个载体去执行,匿名函数的存在形式可以理解为一种变量类型,跟基本类型功能一致,那么函数类型可以直接赋值给变量,也可以定义在函数的参数中,也可以作为返回值
匿名函数是没有return的,最后一行代码就是匿名函数的返回值
val methonAction : (num : Int, str : String) -> String = { num, str ->
println("这是数字 $str 这是字符 $num ")
""
}
methonAction(1, "222")
变长参数
如果想向变长参数里面传入一个array,则需要在array前加入*,这个星号在Kotlin变长参数中叫Spread操作,可以理解为“打散”、“分散”
// vararg 表示可变参数
fun getSum(vararg ints: Int) {
println("show ${ints.size}")
}
fun main() {
getSum(1, 2, 3, 4)
getSum(*intArrayOf(1,2,3,4))
}
默认参数
// 第三个参数有默认值 false
fun setAction(x: Int, y: String, z: Boolean = false) {
println("Hello")
}
fun main() {
// 传入完整的参数
setAction(0, "Hello", true)
// 已经有默认值的参数可不传,使用默认值
setAction(0, "Hello")
}
具名参数
fun setAction(x: Int = 0, y: String, z: Boolean = false) {
println("Hello")
}
fun main() {
// 传入完整的参数
setAction(0, "Hello", true)
// 使用具名参数,只需要传入参数y
setAction(y = "Hello")
}
反引号
kotlin中的反引号主要有两个作用
- 可以解决关键字冲突的问题
- 可以强行将一个不合法的字符变为合法
解决关键字冲突
在java代码定义一个方法,名为is,在Java中是很普通的方法,但是在kotlin中,is属于关键字,在混合调用时,就会报错,这时可以加上反引号解决关键字冲突
public static void is(){
System.out.println("这是is方法");
}
TestJava.`is`()
强行将一个不合法的字符变为合法
例如使用中文命名函数,这是不合法的,但是在加上反引号后,就强行将不合法变成了合法
fun `不合理的命名`(){}