课程地址:https://kaixue.io/kotlin-basic-1/
为项目添加 Kotlin 支持
根目录下的 build.gradle
buildscript {
// 这里配置一个全局版本号
ext.kotlin_version = '1.3.31'
dependencies {
// 这里添加依赖
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
app 目录下的 build.gradle
// 使用 kotlin 的 plugin
apply plugin: 'kotlin-android'
dependencies {
// 添加依赖
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
Kotlin 文件都是以 .kt 结尾的
变量
变量的声明与赋值
var view : View
- 有一个 var 关键字
- 变量名在前,类型在后,冒号分隔
- 结尾不需要分号
Kotlin 空安全设计
非空类型
- Kotlin 中变量是没有默认值的,需要手动初始化
- Kotlin 默认声明变量是非空的,不能赋值为 null
可空类型
- 声明可空变量需要在类型右边加一个问号,被称为可空类型
var name: String? = null
- 调用可空类型变量时不能手动判断是否为空,需要使用 ?. ,被称为「safe call」
view?.setBackgroundColor(Color.RED)
- 还有一种双叹号的写法,被成为「断言式写法」,编译器不再做检查
view!!.setBackgroundColor(Color.RED)
关于空安全,最重要的是记住一点:所谓「可空不可空」,关注的全都是使用的时候,即「这个变量在使用时是否可能为空」。
Kotlin 和 Java 的空安全设计兼容
- Kotlin 中调用 Java 中 @Nullable 变量
name?.length
- Java 中 @Nullable 和 @NonNull 注解可以自动转化为 Kotlin 代码
// Java
@Nullable
String name;
@NonNull
String value = "hello";
// Kotlin
var name: String? = null
var value: String = "hello"
延迟初始化
lateinit 关键字适用于声明时不能赋值,但是能保证使用之前一定会赋值的场景,例如 Activity 中的 View:
lateinit var view: View
override fun onCreate(...) {
view = findViewById(R.id.view)
}
类型推断
可以在声明变量时不指定变量类型,自动推断。
var name = "Daniel"
动态类型指变量的类型在运行时可以改变,类型推断指在代码里不用写变量类型,编译器会自动推断,Kotlin 是静态语言,不支持动态类型。
var 和 val
val 是只读变量,只能复制一次,类似 Java 中的 final。
var 是 variable 的缩写,val 是 value 的缩写
可见性
Kotlin 中的变量默认是 public 的。
函数
函数的声明
fun cook(name: String): Food {
...
}
- 以 fun 关键字开头
- 返回值写在函数名和参数后面
- 没有返回值对应的类型是 Unit,并且可以省略
- 函数参数和返回值类型可以是可空类型
可见性
默认也是 public
属性的 getter/setter 函数
调用属性时其实调用的是它的 getter 和 setter。
var name = "Daniel"
get() {
return field + " nb!!"
}
set(value) {
field = "good " + value
}
- getter / setter 函数有了专门的关键字 get 和 set
- getter / setter 函数位于 var 所声明的变量下面
- setter 函数参数是 value
- 使用 field 表示背后的字段
- val 变量不能重写 setter,但可以重写 getter,而且取值时还能修改
fun main() {
// 虽然 name 是 val,但这里打印的值每次都不一样
println(Dog().name)
}
class Dog{
val name = "dog"
get() {
return field + System.currentTimeMillis()
}
}
类型
基本类型
在 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 中的区别:
- Int 是否装箱根据场合来定
var a:Int = 1 // unbox
var b: Int? = 2 // box
var list: List<Int> = listOf(1, 2) // box
- 数组的写法是有区别的
var array: IntArray = intArrayOf(1, 2) // unbox
- 不可空类型、使用 IntArray、FLoatArray 等不会装箱
- 个人理解,是否装箱取决于背后使用的是不是对象引用以及会不会为空
类和对象
Kotlin 中的类默认是 public 的,可以省略
类的继承使用 : 代替 extends 或者 implement
-
构造方法写法不同
- Java 中会省略默认构造函数
- Kotlin 下面这几种写法都可以
// 省略构造函数的写法 class MainActivity : AppCompatActivity() {} // 等价的写法 class MainActivity constructor() : AppCompatActivity() {} // 更像 Java 的写法 class MainActivity : AppCompatActivity { constructor() { } }
- Kotlin 构造函数使用 constructor 关键字
-
override 的不同
- Java 中使用 @Override 注解
- Kotlin 使用 override 关键字
- Kotlin 中省略了 protected 关键字,Kotlin 中的 override 函数的可见性是继承自父类的
-
Kotlin 中的类默认是 final 的,可以使用 open 关键字解除
- 子类不继承 open 属性
- 子方法会继承 open 属性,可以使用 final 关闭
Kotlin 中也可以用 abstract 关键字声明抽象类,同 Java
Kotlin 中创建对象不需要 new 关键字
类型的判断和强转
- Kotlin 中使用 is 判断类型,并且可以省略强转
fun main() {
var activity: Activity = SubActivity()
if (activity is SubActivity) {
// 强转由于类型推断被省略了
activity.subFun()
}
}
- Kotlin 使用 as 关键字强转(同 Java ),使用 as? 来进行安全强转
// 强转
fun main() {
var activity: Activity = SubActivity()
(activity as SubActivity).subFun()
}
// 安全强转
fun main() {
var activity: Activity = SubActivity()
// 注意安全强转后是可空类型,需要使用 ?. 调用
(activity as? SubActivity)?.subFun()
}
思考题
- 子类重写父类的 override 函数,能否修改它的可见性?
可以放宽,不能收窄,否则多态调用的时候将无法调用子方法,同 Java。
open class Bird {
protected open fun fly(){
print("bird fly...")
}
}
class Eagle : Bird(){
// 可以
public override fun fly(){
print("eagle fly...")
}
}
class Canary : Bird(){
// 报错
private override fun fly(){
print("canary fly...")
}
}
- 以下的写法有什么区别?
activity as? NewActivity
activity as NewActivity?
activity as? NewActivity?
as? 表示安全强转,NewActivity? 表示转换为可空类型,两者并不矛盾,分情况讨论。
activity 值 | null | NewActivity 非空实例 | 其它类型 |
---|---|---|---|
activity as? NewActivity | 不执行 | 转换成功 | 不执行 |
activity as NewActivity? | 转换结果为 null | 转换成功 | ClassCastExecption |
activity as? NewActivity? | 转换结果为 null | 转换成功 | 不执行 |
- 第一种写法只有当 activity 为 NewActivity 非空实例时会做强转,否则不执行;
- 第二种写法是不安全强转,不管 activity 是什么类型,也不管是不是 null 都会强转,转换失败会抛出 ClassCastException;
- 第三种写法也是安全强转,如果 activity 是 NewActivity 的实例或者 null,都会安全转换为 NewActivity? 类型,否则不执行。