本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Kotlin
变量
在Kotlin
中声明变量只允许使用关键字val
和var
。
val (value)
用于声明不可变的变量,相当于Java
中的final
变量。var (variable)
用于声明可变的变量,相当于Java
中的非final
变量。
类型
Kotlin
拥有类型推导机制,声明且赋值后,自动推导成对应类型,且不允许再赋值成其他类型。
但是对一个变量延迟赋值的话,Kotlin
无法自动推导他的类型。这时候就需要显示声明变量类型。
val a: Int = 10
在Kotnlin
中,Int
为首字母大写,Kotlin
完全抛弃了Java
的基础类型,全部使用对象数据类型。
Java基本数据类型 | Kotlin对象数据类型 | 数据类型说明 |
---|---|---|
int | Int | 整型 |
long | Long | 长整型 |
short | Short | 短整型 |
float | Float | 单精度浮点型 |
double | Double | 双精度浮点型 |
boolean | Boolean | 布尔型 |
char | Char | 字符型 |
byte | Byte | 字节型 |
函数
fun largeNumber(param1 : Int, param2 : Int) : Int {
return max(param1, param2)
}
当一个函数中只有一行代码时,Kotlin
允许我们不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可。加上Kotlin
的类型推导机制,以上代码可以简化为
fun largeNumber(param1 : Int, param2 : Int) = max(param1, param2)
逻辑控制
if 条件语句
Kotlin
中的if
语句相比于Java
有一个额外的功能,它是可以有返回值的,返回值就是if
语句每一个条件中最后一行代码的返回值
fun largerNumber(num1: Int, num2: Int): Int {
val value = if (num1 > num2) {
num1
} else {
num2
}
return value
}
语法糖优化精简
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) {
num1
} else {
num2
}
//fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2
when 条件语句
精确匹配
fun getScore(name: String) = when (name) {
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
类型匹配
fun checkNumber(num: Number) {
when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
}
//is关键字就是类型匹配的核心,它相当于Java中的instanceof关键字
when
语句的结构体:
匹配值 -> { 执行逻辑 }
当执行逻辑只有一行代码是,可以省略{}
when
可以不带参数
fun getScore(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}
循环语句
Kotlin
中的区间概念
val range = 0..10
上述代码表示创建了一个0到10的区间,并且两端都是闭区间,用数学的方式表达出来就是[0, 10]
其中,..
是创建两端闭区间的关键字,在..
的两边指定区间的左右端点就可以创建一个区间了。
fun main() {
for (i in 0..10) {
println(i)
}
}
Kotlin
中可以使用until
关键字来创建一个左闭右开的区间
val range = 0 until 10
上述代码表示创建了一个0到10的左闭右开区间,它的数学表达方式是[0, 10)
step
关键字用于跳过其中的一些元素
fun main() {
for (i in 0 until 10 step 2) {
println(i)
}
}
..
和until
关键字都要求区间的左端必须小于等于区间的右端,也就是这两种关键字创建的都是一个升序的区间
降序的区间,可以使用downTo
关键字
fun main() {
for (i in 10 downTo 1) {
println(i)
}
}
类
在Kotlin
中任何一个非抽象类默认都是不可以被继承的,要让一个类可以被继承,需要加上open
关键字
open class Person{
}
继承一个类在Kotlin
中使用冒号
class Student : Person() {
}
构造函数
主构造函数
每个类默认都会有一个不带参数的主构造函数,也可以显式的指明参数。主构造函数的特点是没有函数体,直接定义在类名后面即可。
class Student(val sno: String, val grade: Int) : Person() {
}
如果主构造函数需要编写逻辑,Kotlin
提供了init
结构体,所有主构造函数的逻辑都可以写在里面
class Student(val sno: String, val grade: Int) : Person() {
init {
println("sno is " + sno)
println("grade is " + grade)
}
}
子类中的构造函数必须调用父类中的构造函数。
而子类具体调用父类中的哪个构造函数,在Kotlin
中体现在继承时冒号后的括号。
class Student(val sno: String, val grade: Int) : Person() {
}
//在这里,Person类后面一对空括号表示Student类的主构造函数在初始化的时候会调用Person类的无参构造函数,即使无参数的情况下,括号也不能省略。
调用父类的有参构造函数则如下
open class Person(val name : String, val age: Int) {
//...
}
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
//...
}
//在子类中增加父类拥有的字段时,不加关键字 var 或者 val,因为声明成 val 或 var 的参数,将自动成为该类的字段,与父类中同名的字段冲突。
次构造函数
任何一个类只能有一个主构造函数,但可以有多个次构造函数。次构造函数也可以用于实例化对象,这一点与主构造函数没有什么区别,但次构造函数时拥有函数体的。
Kotlin
规定,当一个类既有主构造函数又有次构造函数时,所有次构造函数必须调用主构造函数(包括间接调用)
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
constructor(name: String, age: Int) : this("", 0, name, age) {
//次构造函数1
}
constructor() : this("", 0) {
//次构造函数2
}
}
次构造函数通过constructor
关键字来定义的。
次构造函数1通过this
关键字调用主构造函数;次构造函数2通过this
关键字调用次构造函数、间接调用主构造函数。
当一个类没有显式定义主构造函数,同时又定义了次构造函数,则继承类时,被继承类不需要加括号
class Student : Person {
constructor(name: String, age: Int) : super(name, age) {
}
}
接口
Kotlin
与Java
一样,是单继承结构,任何一个类只能继承一个父类,但可以实现多个接口。
修饰符
修饰符 | Java | Kotlin |
---|---|---|
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类、子类、同一包路径下的类可见 | 当前类、子类可见 |
default | 同一包路径下的类可见(默认) | 无 |
internal | 无 | 同一模块中的类可见 |
空指针处理
?.
操作符:当对象不为空时正常调用相应的方法,当对象为空时则什么都不做?:
操作符:如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果
标准函数
let
调用let
,对象本身作为参数
fun doStudy(study: Study?) {
study?.let {
it.readBooks()
it.doHomework()
}
}
with
第一个参数可以是一个任意类型的对象,第二个参数是一个Lambda表达式。with
函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回
它可以在连续调用同一个对象的多个方法时让代码变得更加精简
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = with(StringBuilder()) {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
run
区别于with
:run函数通常不会直接调用,而是要在某个对象的基础上调用,
run`函数只接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().run {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
apply
区别于run
:apply
函数无法指定返回值,而是会自动返回调用对象本身
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().apply {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
}
println(result.toString())
静态方法
object关键字
定义一个类为单例类、里面所有方法都为静态方法。
object Util {
fun doAction() {
println("do action")
}
}
companion object
定义普通类中的方法为静态方法。
class Util {
fun doAction1() {
println("do action1")
}
companion object {
fun doAction2() {
println("do action2")
}
}
}
doAction2()
方法其实也并不是静态方法,companion object
这个关键字实际上会在Util
类的内部创建一个伴生类,而doAction2()
方法就是定义在这个伴生类里面的实例方法。只是Kotlin会保证Util
类始终只会存在一个伴生类对象,因此调用Util.doAction2()
方法实际上就是调用了Util
类中伴生对象的doAction2()
方法
以上两个关键字都不是真正的静态方法,仅仅是模拟静态方法
注解 @JvmStatic
而如果我们给单例类或companion object
中的方法加上@JvmStatic
注解,那么Kotlin编译器就会将这些方法编译成真正的静态方法。
@JvmStatic
注解只能加在单例类或companion object
中的方法上
class Util {
fun doAction1() {
println("do action1")
}
companion object {
@JvmStatic
fun doAction2() {
println("do action2")
}
}
}
顶层方法
顶层方法指的是那些没有定义在任何类中的方法
kt
文件中直接定义的方法,在kotlin任意位置,直接使用方法名调用。
java中调用顶层方法:xxxKt.methodName
延迟初始化
lateinit:声明变量时不初始化变量,后续初始化。
::variable.isInitialized:用于判断variable
变量是否已经初始化
密封类
sealed class
当在when
语句中传入一个密封类变量作为条件时,Kotlin编译器会自动检查该密封类有哪些子类,并强制要求你将每一个子类所对应的条件全部处理
密封类及其所有子类只能定义在同一个文件的顶层位置,不能嵌套在其他类中,这是被密封类底层的实现机制所限制的。
扩展函数
扩展函数表示即使在不修改某个类的源码的情况下,仍然可以打开这个类,向该类添加新的函数
运算符重载
operator
关键字
语法糖表达式 | 实际调用函数 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b) |
a++ | a.inc() |
a-- | a.dec() |
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a == b | a.equals(b) |
a > b | a.equals(b) |
a < b | a.equals(b) |
a >= b | a.equals(b) |
a <= b | a.compareTo(b) |
a..b | a.rangeTo(b) |
a[b] | a.get(b) |
a[b] = c | a.set(b, c) |
a in b | b.contains(a) |