Kotlin之基础语法
语言简介
kotlin是一门新语言,它有自己的编译器,能将kotlin代码编译成class文件,因此能被JVM识别,所以kotlin也是一门解释型语言,同java一样。JVM并不关心class文件来自于Java还是Kotlin,只要是符合规定的class文件,它都能识别。
语法特点
- Kotlin每一行代码的结尾是不用加分号的。
- Kotlin完全抛弃了Java中的基本类型,继而全部使用对象数据类型。
- Kotlin主要有类(class)、文件(file)、接口(interface)、密封接口(sealed interface)、数据类(data class)、枚举类(enum class)、密封类(sealed class)、注解(annotation)、单例类(object)。
- File通常是用于编写Kotlin顶层函数和扩展函数的。
常用的关键字
- is关键字:可以用来判断对象的类型,相当于java的instanceof。
-
val a = 10 val b = a is Int
-
- Unit
- 也是一种类型,就像Int一样,只是它不代表任何信息。
- 它的实例只有一个,可写为()。
- ?:
- 它叫做Elvis运算符,或者null合并运算符。
- 作用:可以用来给可空类型的变量指定为空情况下的值。
-
var test: Int? = null test = test ?: 1
-
- ?.
- 对象方法调用,如果对象为空,则不进行任何操作;否则则调用该方法。
-
val dog: Dog? = null dog?.test()
-
- 对象方法调用,如果对象为空,则不进行任何操作;否则则调用该方法。
- ?:
- 如果对象为空,则运行右侧;否则运行左侧。
-
val a: Int? = null val b = a ?: 0
-
- 如果对象为空,则运行右侧;否则运行左侧。
- !!
- 非空断言,放在一个变量后面断定该变量不可能为空。如果变量为空了就会报错。
- 实际写代码时建议不要用,除非万不得已。
-
val a: Int? = null val b = a!! == 2
-
- as关键字
- 可以进行强转。
- 可以用来给包起别名。如果包名出现命名冲突,可以用来消除歧义。
-
import com.google.gson.Gson as MyGson
-
变量
- 声明变量:使用var和val。
- val:不可变变量。等同于Java的final变量。
- 好的习惯:除非一个变量明确允许被修改,否则就应该把定义为final的。因此,建议永远优先使用val来声明一个变量,当val不满足需求时再使用var。
-
val s: String = "hello"
- var:可变变量。
-
var s: String
-
- getter和setter:当你在声明一个类的属性时,编译器会自动生成对应的getter、setter方法,但是你也可以主动声明这两个方法来实现一些特殊的逻辑。
- val属性只有getter方法,而var变量可以有getter和setter方法。
- private属性会自动省略getter和setter方法。
-
val Context.`package`: String get() = this.packageName
-
- val:不可变变量。等同于Java的final变量。
- 可空类型:
- 在类型之后加一个问号?,表示该变量可以为空。
-
val name: String? = null
-
- 在类型之后加一个问号?,表示该变量可以为空。
- 出色的类型推导:编译器可以在不显式声明类型的情况下,自动推导出它所需要的类型。
- 虽然Kotlin的类型推导机制很强大,但是也有它推导不了的情况,比如对一个变量延迟初始化,这时就无法推导,必须在定义时显式地声明变量类型才可以。
-
val s = "hello" println("s的类型是:${s.javaClass.name}") 输出: s的类型是:java.lang.String
- 声明函数返回值类型:
- 如果没有声明返回的类型,函数会默认返回Unit类型。
-
fun sum(x: Int, y: Int): Int { return x + y }
- 以上代码可以用表达式函数体来优化。
- 表达式函数体:用单行表达式与等号的语法来定义函数。
- 与之对应,普通的函数声明则可叫做代码块函数体。
-
fun sum(x: Int, y: Int) = x + y
- 在使用表达式函数体可以不声明返回值类型。但这不代表只要使用了表达式函数体就可以不声明返回值类型。
- 如下,编译器不能针对递归函数的情况推导类型。
-
fun foo(n: Int): Int = if (n == 0) 1 else n * foo(n - 1)
- 总结:哪些情况需要显式声明类型?
- ①它是函数的参数:必须。
- ②它是一个非表达式定义的函数:除了返回Unit,其他情况必须。
- ③它是一个递归的函数:必须。
- ④它是一个公有方法的返回值:为了代码可读性和输出类型的可控性,建议。
- ⑤除了以上情况,就可以不显式声明,除非碰到意外。
函数
- 函数-function,方法-method,它们是同一个东西,只是叫法习惯不同。在c、c++中习惯叫做函数,在Java中习惯叫做方法,而在Kotlin中习惯叫做函数。
- 定义:使用fun关键字来定义函数。
- 格式:
-
fun methodName(param1: Int, param2: Int): Int { }
-
- 函数默认参数值:
-
fun test(num: String = "wangwang", age: Int = 10) { println("name = $name, age = $age") }
-
函数可变参数
- 使用varargs关键字来定义。
- 类似于Java的“…”,不同的是Java的可变参数必须是最后一个参数,而kotlin没有这个限制。
- 它们都可以在函数体中意数组的方式来使用可变参数变量。
-
fun test(vararg nums: Int, count: Int) { nums.forEach { println(it) } } test(1, 2, 3, count = 4) 输出: 1 2 3
-
- 可以使用星号*来传入外部的变量作为可变参数的变量。
-
val arr = IntArray(3) test(*arr, count = 3) 输出: 0 0 0
-
逻辑控制
if表达式
- kotlin的if语句与Java不同的是它可以有返回值。
-
val random = Random(47) val a = random.nextInt(100) + 1 val b = random.nextInt(100) + 1 val num = if (a > b) a else b println("a = $a, b = $b, num = $num") 输出: a = 97, b = 59, num = 97
when表达式
- when表达式类似于switch语句,有when关键字开始,用大括号包含多个逻辑分支,每个分支用“->”连接,不需要break,从上到下匹配,一直匹配完为止,否则执行else分支的逻辑,else分支类似于default。
- 对比Java的switch语句,switch只能使用整型、短于整型或字符串的变量。
- when表达式同样可以有返回值。
-
val random = Random(47) val a = random.nextInt(100) + 1; val b = when(a) { 100 -> "完美" 60 -> "及格" else -> "不及格" }
-
- 每个分支都有返回值,最终返回值的类型是所有分支相同的返回类型,或者公共类型。
- 分支如果只有一行代码,可以省略{}。
-
fun b(a: Int) = when(a) { 100 -> "完美" 60 -> "及格" else -> "不及格" }
- when关键字的参数可以省略。
-
fun b(a: Int) = when { a == 100 -> "完美" a == 60 -> "及格" else -> "不及格" }
-
- 表达式可以组合。
-
fun b(is100: Boolean, a: Int) = when { is100-> "完美" a == 60 -> "及格" else -> "不及格" }
-
for循环
- kotlin中的while和do-while与Java中的一样。
- kotlin的for循环,可以声明i的类型:
-
for (i: Int in 1..5) { println(i) } 输出: 1 2 3 4 5
-
- 范围表达式:
- Range表达式是通过rangeTo函数实现的,通过“..”操作符与某种类型的对象组成,左闭右闭,[0, 10]。除了整数的基本类型之外,该类型需实现java.lang.Comparable接口。
- 当对整数进行for循环时,还可以使用step函数来定义步长。
-
for (i: Int in 1..5 step 2) { println(i) } 输出: 1 3 5
-
- 倒序:使用downTo方法实现。
-
for (i: Int in 5 downTo 1 step 2) { println(i) } 输出: 5 3 1
-
- 半开区间:左闭右开,[0,10),使用until函数。
-
for (i in 1 until 5) { println(i) } 输出: 1 2 3 4
-
- 打印字符a-z:
-
for (i in 'a'..'z') { println(i) }
-
- 当对整数进行for循环时,还可以使用step函数来定义步长。
- Range表达式是通过rangeTo函数实现的,通过“..”操作符与某种类型的对象组成,左闭右闭,[0, 10]。除了整数的基本类型之外,该类型需实现java.lang.Comparable接口。
- 用“in”来检查成员关系
- 用来检查一个元素是否是一个区间或集合中的成员。
-
val isIn = "a" in listOf("a", "b", "c") println("a是否在列表中:$isIn") 输出: a是否在列表中:true
-
- 在in前加上“!”
-
val isIn = 1 !in listOf(1, 2, 3) println("1不在列表中:$isIn") 输出: 1不在列表中:false
-
-
val isIn = "kot" in "abc".."xyz" println("isIn = $isIn") 输出: isIn = true
- 任何提供迭代器(iterator)的结构都可以用for语句进行迭代。
- 还可以通过调用一个withIndex方法提供一个键值元组。
-
for ((index, value) in arrayOf(100, 28, 73).withIndex()) { println("当前元素为:$value, 下标为:$index") } 输出: 当前元素为:100, 下标为:0 当前元素为:28, 下标为:1 当前元素为:73, 下标为:2
-
- 用来检查一个元素是否是一个区间或集合中的成员。
- 中缀表达式:
- 如in step downTo until等方法都不是用点进行调用的,而是使用中缀表达式来调用。
- 如to方法,他可以返回一个Pair:
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
。- to这种形式定义的函数被称为中缀函数。
- 由于to会返回Pair这种键值对的数据结构,因此常与map一起使用。
- 使用:
- A 中缀方法 B
- 定义一个中缀函数必须满足:
- ①该中缀函数必须是某个类型的扩展函数或成员方法。
- ②该中缀函数只能有一个参数。
- ③中缀函数的参数不能有你·默认值。
- ④该参数不能是可变参数。
- 自定义中缀函数:
- 中缀函数除了可以使用中缀表达式那样调用,也可以像正常函数那样调用。
-
class Dog { infix fun show(name: String) { println("name is $name") } } //调用: val dog = Dog() dog show "wangwang" 输出: name is wangwang
方法和成员引用
- Kotlin的一种特殊语法:使用双冒号“::”对某个类的方法进行引用。
- Dog类:我们定义了一个Dog类,调用时我们把常规的写法和双冒号的写法做了对比,实践发现爽冒号可以对类的构造方法、成员变量和成员方法创建引用变量,并通过该变量可以创建了Dog实例、调用dog实例的属性以及调用dog实例的方法。
-
class Dog(val name: String) { fun run() { println("$name start run!") } } //调用: fun main() { //①正常语法编写: val dog1 = Dog("lala") println("Dog1's name is ${dog1.name}.") dog1.run() //②双冒号语法编写: //引用类的构造方法 val getDog = ::Dog val dog2 = getDog("wangwang") //引用类的成员变量 val getName = Dog::name val name = getName(dog2) println("Dog2's name is $name.") //引用类的成员方法 val execRun = Dog::run execRun(dog2) } 输出: Dog1's name is lala. lala start run! Dog2's name is wangwang. wangwang start run!
-
- Dog类:我们定义了一个Dog类,调用时我们把常规的写法和双冒号的写法做了对比,实践发现爽冒号可以对类的构造方法、成员变量和成员方法创建引用变量,并通过该变量可以创建了Dog实例、调用dog实例的属性以及调用dog实例的方法。
- 通过狗狗名称长度进行过滤:
-
fun filterDogs(dogs: List<Dog>, filter: (Dog) -> Boolean): List<Dog> { val res = mutableListOf<Dog>() for (dog in dogs) { if (filter(dog)) { res.add(dog) } } return res } class Dog(val name: String) { fun filterName(dog: Dog): Boolean { return dog.name.length > 4; } } val dogs = arrayListOf(Dog("lala"), Dog("wangwang"), Dog("gaga")) println("规则:名字长度小于等于4的将被过滤。\n过滤前:") dogs.forEach { println(it.name) } //调用: val filterDogs = filterDogs(dogs, Dog("")::filterName) println("过滤后:") filterDogs.forEach { println(it.name) } 输出: 规则:名字长度小于等于4的将被过滤。 过滤前: lala wangwang gaga 过滤后: wangwang
-