Kotlin 学习笔记之 —— Kotlin 的变量、函数和类型

课程地址: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 = 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()
}

思考题

  1. 子类重写父类的 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...")
    }
}
  1. 以下的写法有什么区别?
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? 类型,否则不执行。

你可能感兴趣的:(Kotlin 学习笔记之 —— Kotlin 的变量、函数和类型)