Kotlin基础二:属性和控制流

134 阅读5分钟

前言

学习Kotlin中属性是经常用到的基础知识,Kotlin中属性的声明和使用 可空属性声明 可空类型的安全检测。if when 表达式,for while do..while循环,break continue控制循环终止和跳转。

属性

用关键字val和var来声明属性。val是只读,var是可变的。默认情况下,属性必须设置初始值:

val/var propertyName: propertyType= initValue
[getter]
[setter]

如果能从初始值或者[getter]方法返回值推断出类型,可以省略属性类型。初始值 [getter] 和[setter]方法都是可选的。

val a:String = "kotlin"
// 推断出属性的类型是String则省略
val b = "kotlin"
val c:String
get() = "kotlin"
// 推断出[getter]方法返回值是String则省略
val d
get() = "kotlin"

Kotlin中[getter]和[setter]允许自定义的。

// 单行表达式
val a = "info"
// 单行表达式
val b get() = "info"
// 有额外的逻辑,自定义[getter]
val c:String
get(){
...业务相关
return x
}

用val关键字声明只读属性,不允许自定义[setter]方法。

val a = "kotlin"
// 不允许自定义,会语法报错(编译不过代码报红)
set(value){}
var b = "kotlin"
// 允许自定义[setter]
set(value){}
var b = "kotlin"
    set(value) {
        field =  "123"
    }
    
fun test() {
   println(b)// kotlin
   b="1"
   println(b)// 123
}

AndroidStudio中选择Tools->Kotlin->Show Kotlin ByteCode。点击Decompile按钮。

image.png 最终反编译得到

var a = "kotlin"
val b = "kotlin"// java中是final 修饰的


@NotNull
private String a = "kotlin";
@NotNull
private final String b = "kotlin";

field字段

在Property.kt的File文件中

var a:String = "kotlin"
set(value){
    if(value.length > 0){
        field = value
    }else{
        field = ""
    }
}

field字段只能在我们自定义的[setter]方法的访问器中使用。理解为"幕后引用"

var a:String = "kotlin"
set(value){
    a = value // 应该用field不能用a,否则死循环抛出StackOverFlowError异常。其实编译器左侧会有提示这里循环调用了
}
fun main(){
    a = "java"
    println(a)
}
抛出异常:
java.lang.StackOverflowError

幕后字段field的引入,在[setter]方法的访问器中直接给filed赋值,就不会再去调用属性的[setter]方法,规避了这个问题。

可空属性

当声明一个属性可为空,在属性的类型后加?

// 属性可为空
var a:String? = null
// 属性不为空
var b:String = "kotlin"

如果给一个不可为空的属性赋值null,kotlin编译器会报语法错误。

var a = "kotlin"
var b:String? = "java"
fun main(){
a = null // 报错
b = null // 正确
}

延迟初始化属性

kotlin中使用lateinit关键字完成一个属性的延迟初始化

    private lateinit var a:String
    fun test() {
        a = "kotlin"
        if (::a.isInitialized){
            println(a)
        }
    }

在初始化前访问一个lateinit 修饰的属性会抛出lateinit property language has not bean initialized的异常。可以使用::propertyName.isInitialized方法判断属性是否已经初始化,::是一种绑定的语法。 用lateinit修饰的属性类型不可设置可空类型,否则编译器会报语法错误

private lateinit var a:String // 正确
private lateinit var a:String? // 错误

属性空类型检查

空指针异常(NullPointException),通常我们的判空:

if(property != null){
    // ...业务
}

Kotlin中访问可空属性两种方式:一种断言双感叹号!!,一种是?. 断言通常是你确定不空。一般使用后者?.

var a:String? = "kotlin"
a!!.length // 不推荐使用
a?.length // 推荐使用

if语句

Kotlin中不仅是逻辑判断,还可以作表达式,这是一个很好的语法糖:

fun test() {
    val a = 1
    val b = 3
    val max:Int = if (a>b){
        println(a)
        a
    }else{
        println(b)
        b
    }
    println(max)
}

用if表达式取较大者,在Java中可以用三元运算符。if代码中只有一行表达式,我们可以省略花括号。通过Kotlin编译器推断出if表达式的返回值类型。

fun test() {
    val a = 1
    val b = 3
    val max:Int = if (a>b){a}else{b}
    // 简写,省略{}
    val max1:Int = if (a>b) a else b
    // 推断出结果是Int
    val max2 = if (a>b) a else b
}

when语句

也可以是表达式

fun test() {
    // 作为语句
    when (a) {
        "kotlin" -> { println("the kotlin") }
        "java" -> { println("the java") }
        "ios" -> { println("the ios") }
    }
    // 作为表达式,需要有else,除非编译器识别到所有情况,则可省略else块
    val result = when (a) {
        "kotlin" -> { println("the kotlin") }
        "java" -> { println("the java") }
        "ios" -> { println("the ios") }
        else -> { println("the unknown") }
    }
    // 满足条件块中只有一行表达式,可省略{}
    val result2 = when (a) {
        "kotlin" -> println("the kotlin")
        "java" -> println("the java")
        "ios" -> println("the ios")
        else -> println("the unknown")
    }
}

可以使用(in)或者(!in)的区间作为条件分支,也可以使用is。(在kotlin中关键字in是一个操作符,a in b 相当于b.contains(a),is关键字去到了java中的instanceof关键字)

var number = 2
var type:Nothing?=null
fun test() {
    // 使用区间作为条件判断
    when(number){
        in 0..10 ->{}
        !in 10..20->{}
        else ->{}
    }
    // 使用is作为条件判断
    when(type){
        is String ->{}
        is Int ->{}
        else ->{}
    }
}

Kotlin1.3版本后,when后变量可以是一个表达式,可以从表达式中推断出变量的类型:

when (type = getType()) {
    "kotlin" -> println("the kotlin language")
    "java" -> println("the java language")
    "ios" -> println("the ios language")
    else -> println("the else logic")
}

for循环

提供了迭代器(iterator)的对象都可用for循环

for(item:Int in ints){...}

对一个区间进行for循环

//[0,10]闭区间
for(i:Int in 0..1){...}
// [0,10)开区间
for(i:Int in 0 until 10){...}

通过索引遍历一个数组或者一个list,用_Arrays.kt文件提供的扩展属性indices或者扩展方法withIndex()

// 扩展属性
public val <T> Array<out T>.indices: IntRange
    get() = IntRange(0, lastIndex)

for(i in array.indices){...}

// 扩展方法
public fun <T> Array<out T>.withIndex(): Iterable<IndexedValue<T>> {
    return IndexingIterable { iterator() }
}
for(i in array.withIndex()){...}

map常用的遍历方法:

// 遍历key
for(key in map.keys){...}
// 遍历value
for(value in map.values){...}
// 遍历key和value
for((key,value) in map){...}

(key,value)是结构声明。

while和do..while循环

// 先执行判断条件
while(a<b){...}
// 先执行循环体,再执行条件判断
do{...}while(a<b)

break和continue

控制循环跳转的关键字。break终止直接包含它的循环体,continue直接跳过本次循环,进入下次循环

fun main(){
    for(i in 0..2){
        if(i ==1){
            break
        }
        println(i)
    }
    for(i in 0..2){
        if(i == 1){
            continue
        }
        println(i)
    }
}

总结

主要是Kotlin中属性的声明和使用 可空属性声明 可空类型的安全检测。if when 表达式,for while do..while循环,break continue控制循环终止和跳转。