kotlin笔记-基本语法

240 阅读3分钟

写在开头:前段时间阅读KT重构后的LeakCanary源码,发现对KT的许多知识点有所遗忘,决定再次从头开始系统学习KT,并以文章记录,使自己记忆和理解的更加深刻

基础数据类型

基本类型名称kotlin数据类型Java数据类型转换为该类型
整型Intint和IntegertoInt
长整型Longlong和LongtoLong
浮点型Floatfloat和FloattoFloat
双精度Doubledouble和DoubletoDouble
布尔型Booleanboolean和Boolean
字符型CharchartoChar
字符串StringStringtoString

基本数据类型包括如:

  1. 数值类型:整形,长整形,浮点型,双精度
  2. boolean
  3. 字符型
  4. 字符串

Java语言中有两种数据类型:

  1. 基本类型,如int,double等
  2. 引用类型,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

声明变量

KotlinJava
varvar testVar : Int = 1private int testVar = 1;
valvar testVal : Int = 1private final int testVal = 1;
constconst val testConst = 1public static final int testConst = 1;

数组

基本数组类型名称初始化
整型数组IntArrayintArrayOf
长整型数组LongArraylongArrayOf
浮点数组FloatArrayfloatArrayOf
双精度数组DoubleArraydoubleArrayOf
布尔型数组BooleanArraybooleanArrayOf
字符数组CharArraycharArrayOf

数组的创建

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中的反引号主要有两个作用

  1. 可以解决关键字冲突的问题
  2. 可以强行将一个不合法的字符变为合法

解决关键字冲突

在java代码定义一个方法,名为is,在Java中是很普通的方法,但是在kotlin中,is属于关键字,在混合调用时,就会报错,这时可以加上反引号解决关键字冲突

public static void is(){
    System.out.println("这是is方法");
}

TestJava.`is`()

强行将一个不合法的字符变为合法

例如使用中文命名函数,这是不合法的,但是在加上反引号后,就强行将不合法变成了合法

fun `不合理的命名`(){}