Kotlin 实际应用
声明
这个部分主要是说明 Kotlin 变量、方法、类的声明,同时包含一些关于 Kotlin 的类型。
变量
- var 为定义一个可以多次赋值的关键字;val 为定义只能赋值一次的关键字,相当于 java 中的 final。关键字后紧跟着变量名,其后是
:
再后面是类型,最后就是值。「Kotlin 可以进行类型推断」
val name: String = "text"
var text: String = ""
// 使用类型推断
val name = "text"
var text = ""
- 由于 Kotlin 的设计保证了其空安全的特性,保证了每个变量不能为空,可有些时候某个变量可能会为空怎么办?答案就是在声明的类型后面加上 ? ,就告诉编译器这个变量是可空变量。
var listener: ClickListener? = null
需要注意的是,如果定义一个变量为可空变量,那么在引用它时,也要加上空安全调用符,或你虽然定义的是可空,但某些时候你明确知道它不可能是空的,这时候要加上非空断言。
listener?.onClick() //空安全调用相当于在调用时加上了 if 为空的判断
listener!!.onClick() // 不做检查,如果为空,程序崩溃
在 Java 代码中被 @Nullable 所修饰的变量,在使用 Kotlin 调用时等同于调用 Kotlin 的可空变量,需要加上安全调用符号 ?
或非空断言 !!
。
- 在某些时刻,我们需要定义一个非空变量,同时我们也没有办法在定义的时候给出初始值,这时候就需要 lateinit 这个关键字了。先声明出这个变量,但延迟初始化,或者说把初始化变量的时机交给开发者。但如果没有进行初始化就进行调用,也是会报错的。
lateinit var view: View
override fun onCreate(...) {
...
👇
view = findViewById(R.id.tvContent)
}
方法「函数」
- 在 Kotlin 中声明方法,是通过 fun 关键字进行的,同时将返回值类型写在方法名的最后。具体语法如下
fun setOnClickListener(listener: ClickListener) {
// fun setOnClickListener(listener: ClickListener): Unit { 省略返回值
this.listener = listener
}
fun setNumber(number: Int): Int {
return number * 2
}
从这个例子中,可以看出来方法的声明和变量的声明有些类似。其中参数的声明也是几乎与声明变量等同的。在 Java 中如果一个方法的返回类型是 void,是需要声明出来的,在 Kotlin 中是可以省略的,不过也有一个关键字 Unit 与 void 相对应。
- 在 Kotlin 中,方法的参数是可以有默认值的。具体的写法就是在参数的类型后面直接赋值。在调用它时默认值可以不写。
fun setNumber(number: Int = 1): Int {
return number * 2
}
setNumber()
- 当一个方法的参数有多个时,可以为多个参数分别设置默认值,或选择性的设置默认值。不过需要注意的时,如果没有默认值的参数不是在参数列表的前面,在调用时需要显性的声明出字段名。
// 没有默认值的参数不在前面
fun setNumber(number: Int = 1, scale: Int): Int {
return number * scale
}
setNumber(scale = 2)
//没有默认值的参数在前面
fun setNumber(scale: Int, number: Int = 1): Int {
return number * scale
}
setNumber(2)
- 与声明可空变量相同,在声明方法时,无论是参数还是返回值如果是可能为空,只要在类型后加上 ? 即可。
fun setInfo(avatar: String, name: String?): Info? {
...
}
- Kotlin 中对方法的声明还可以进一步的简化,具体做法就是用 = 将大括号及 return 关键字省略。而对于没有返回值的,也是用 = 连接实际执行方法的语句。
fun area(width: Int, height: Int): Int {
return width * height
}
// 简写如下
fun area(width: Int, height: Int): Int = width * height
fun sayHi(name: String) {
println("Hi " + name)
}
// 简写如下
fun sayHi(name: String) = println("Hi " + name)
类型
Kotlin 中也有类型之分,某些时候没有声明出来,是因为编译器能够自动推断。与 Java 类似,Kotlin 也有一些基本类型:
var number: Int = 1 // 类似的还有 Double Float Long Short Byte
var c: Char = 'c'
var b: Boolean = true
var array: IntArray = intArrayOf(1, 2) // 类似的还有 FloatArray DoubleArray CharArray 等
var str: String = "string"
但与 Java 又不同的是,Kotlin 中的基本类型都是对象,所以会有一定的装箱拆箱动作。而稍稍复杂的是,并不是所有情况下都是需要装箱拆箱,比如
var a: Int = 1 // unbox
var b: Int? = 2 // box
var list: List<Int> = listOf(1, 2) // box
Kotlin 在语言层面简化了 Java 中的 int 和 Integer,但是我们对是否装箱的场景还是要有一个概念,因为这个牵涉到程序运行时的性能开销。
因此在日常的使用中,对于 Int 这样的基本类型,尽量用不可空变量。
类
主要说明类、接口。
- 与 Java 相同,声明一个对象也是用 class 这个关键字。不过 Kotlin 中声明类时默认的访问权限就是 public,同时还是被 final 所修饰的。
注意:也不是所有的类都会被加上 final 关键字,抽象类就不会,因为抽象类是不能实例化的,是必须要被实现的。
class Person {
}
- 声明接口则跟 Java 完全相同。
interface Teacher{
}
- 在 Kotlin 中省去了 extends 和 implements 两个关键字,取而代之的是
:
如果需要继承或实现某个接只需要在:
写出响应的类名、接口名即可,如果存在多个使用,
进行分割。
class Student : Person(), Child {
}
open class Person {
}
interface Child {
}
由于默认的 Kotlin 声明的类是被 final 所修饰的,所以如果想继承某个类,需要在那个类声明的 class 关键字前加上 open 关键字。
构造函数
与 Java 相同,Kotlin 也有构造函数或者说叫构造器的概念,不同的是,Kotlin 把构造函数分为两类,主构造函数和次级构造函数。
在上面的例子,我省去了类的主构造器的声明,就像 Java 中可以无参构造函数是一样的。
class Person constructor(){
}
当然这样写,编译器会提示把 constructor 省略。
特性「特殊之处」
- 声明变量
- get\set
-
集合的可修改和不可修改
-
Class 特性
- Object 单例
- data 数据类 toString 等
高阶函数
一个函数的参数或是返回值类型是函数类型的,那么这个函数就是高阶函数。
同时,在定义时可以设置这个函数为扩展函数或是 suspend 函数。
函数类型作为参数
fun doSome(number: Int, action: (n: Int) -> Int): Int {
return action.invoke(number + 2)
}
@Test
fun test_run_param() {
val doSome = doSome(10, fun(n: Int): Int {
return n * n
})
println("do some result: $doSome")
val doSome1 = doSome(10) { it * it }
println("do some result: $doSome1")
}
应用场景:需要对稍候执行的内容进行自定义的场景下,比如:listener 的回调方法就可以使用高阶函数进行传递。
函数类型作为返回值
fun getCostCaculator(delivery: Delivery): (Order) -> Double {
if (delivery == Delivery.standard) {
return { order -> order.goodCount * 10.4 + 10 }
}
return fun(order: Order): Double { return order.goodCount * 25.5 + 20 }
}
@Test
fun test_run_return() {
val costCaculator = getCostCaculator(Delivery.standard)
val invoke = costCaculator.invoke(Order(10))
}
通过将返回值设置为函数类型并返回一个函数,这种函数也是高阶函数。if 判断内的 return 为简写。
应用场景:不需要立即执行 invoke 方法时可以使用这个函数。当需要立即调用返回的函数时不如返回一个包含这个函数的对象。
问题:既然我能返回一个函数类型,而且这个函数类型还没有被执行 invoke 方法,为什么我不返回 这个函数类型的 invoke 方法后返回 invoke 的返回值?而且返回一个 invoke 方法最终也是会被调用的,如果不被调用,那么这个返回的函数就是无意义的。