• Kotlin 基础学习 (一) 关键字


    Kotlin
    线上编写练习
    Kotlin官网提供了 PlayGround 供大家线上尝试
    地址:https://play.kotlinlang.org/

    原文:https://www.jianshu.com/p/ae5f15ceb9b0

    前言

    本来没打算把关键字介绍放在第一部分,可是发现,如果不放在这儿,后面很多用到关键字的地方会有很多不明白不了解的情况。

    所以还是把关键字的介绍放在了第一部分,不需要全部记住,但是在后面看到的时候,可以随时翻到这篇文章,可以做一个简单的了解。

    本文简单的介绍了Kotlin中使用的部分常用关键字。
    希望对大家初步了解Kotlin有所帮助。

    关键字

    Kotlin 官网文档
    Keywords
    https://kotlinlang.org/docs/keyword-reference.html

    Kotlin 的关键字大致可以分为 三类

    • Hard keywords 硬关键字
      任何情况下都被优先解析为关键字,不可以作为任何标识符。
    • Soft keywords 软关键字
      在某些起作用的上下文中作为关键字,在其不起作用的上下文中可以作为标识符使用。
    • Modifier keywords 修饰符关键字
      可以作为修饰符的关键字,也可以在其它地方用作标识符
    • Special identifiers 特殊标识符
      可以由编译器在特定上下文中定义,它们可以在其他上下文中用作常规标识符
    • Operators and special symbols 运算符和特殊符号
      一些常见运算符和特殊符号

    硬关键字 Hard Keywords

    Kotlin 官网文档
    Hard Keywords
    https://kotlinlang.org/docs/keyword-reference.html#hard-keywords

    下面的这些符号任何情况下都被优先解析为关键字,不可以作为任何标识符。

    // 数据类型转换 
    val obj: Any = 1
    // 可能会抛出 java.lang.ClassCastException,可以考虑 is 或者 as? 替代
    val intValue: Int = obj as Int 
    
    // 处理Nullable
    val intValueNullable: Int? = obj as? Int
    // is 之后可以直接使用对应属性
    if (obj is Int) {
        print("${obj.plus(1)}")
    }
    ==================================================
    // import 别名设置
    import com.unluck.ktdemo.DemoClass
    import com.unluck.ktdemo.bean.DemoClass as DemoBean // 同时引入DemoClass会冲突,可以考虑通过别名引入
    
    //  空安全,但是需要处理 Nullable
    val obj: Any? = null
    val intValueNullable: Int? = obj as? Int
    
    // break 终止 for
    for (i in 1..100) {
        if (...) break
    }
    
    // break to labels 可以声明labels 终止指定的for循环
    breakToThis@ for (i in 1..100) {
        for (j in 1..100) {
            if (...) break@breakToThis // 终止 breakToThis@ for(i)
        }
    }
    
    // 声明类 DemoClass 
    class DemoClass {} 
    // 声明类 MainActivity 并继承自 AppCompatActivity
    class MainActivity : AppCompatActivity() 
     // 声明类 DemoTextView 并继承自 AppCompatTextView
    class DemoTextView(ctx : Context) : AppCompatTextView(ctx)
    // 声明数据类 DemoBean 并实现 Serializable 接口
    data class DemoBean(val name: String) : Serializable
    
    // 同break使用方式类似
    for (i in 1..100) {
        if (...) continue
    } 
    
    // continue to labels 可以声明labels 继续指定的for循环的下一个循环
    continueToThis@ for (i in 1..100) {
        for (j in 1..100) {
            if (...) continue@continueToThis
        }
    }
    
    do {
        ...
    } while (true)
    
    • else 定义一个 if 表达式 的 false 时的执行分支.
    if (...) 
        ... 
    else 
        ...
    
    val bool : Boolean = false
    
    for (i in 1..100) {
        print("print i = $i")
    } 
    
    fun mainFunction() {
        ...
    }
    
    if (i == 0) ... 
    
    // for 迭代
    for (i in 6 downTo 0 step 2) {
        println(i)
    }
    
    // check in range
    if (3 in 0..4)
    
    // check contains
    val list = arrayListOf<Any>()
    if ( "1" in list ) {
        ...
    }
    
    // when branch
    val num = 3
    when {
        num in 0..4 -> print("the num $num is in range 0-4")
        else -> {}
    }
    
    // 逆变参数
    interface Comparable<in T> {
        operator fun compareTo(other: T): Int
    }
    
    • !in
      • 用作 infix 中缀操作符检查值不属于 range, 集合类, 或者其它 定义了 ‘contains’ 方法 的实体类.
      • 一般就是用于 when 表达式的不属于,和上面用于 when 表达式的含义相反.
    // check not in range
    if (6 !in 0..4)
    
    // check contains
    val list = arrayListOf<Any>()
    if ( "1" !in list ) {
        ...
    }
    
    // when branch
    val num = 7
    when {
        num !in 0..4 -> print("the num $num is not in range 0-4")
        else -> {}
    }
    
    
    interface Comparable<in T> {
        operator fun compareTo(other: T): Int
    }
    
    // check value typecasts
    if ( obj is Int) 
        ...
    
    // when branch 
    when (value) {
        is GroupMsg -> print("this is group message")
        else -> print("other message")
    }
    
    
    • !is

      • 跟上面is功能相对
    • null 就是null啊,表示对象没有任何对象引用.

    val nullableValue : Any? = null
    
    // 全局单例
    object SingletonClass {
        var mInstanceValue = 999
    
        fun singletonFunString() = "Singleton Class"
    }
    
    // 伴生对象
    class DemoUtil {
        companion object {
            @JvmStatic
            fun companionObjectFun() = "this is companion object function"
        }
    }
    
    // 对象表达式 -> 某个Any对象
    val helloWorld = object {
        val hello = "Hello"
        val world = "World"
        // object expressions extend Any, so `override` is required on `toString()`
        override fun toString() = "$hello $world"
    }
    
    // 对象表达式 -> 实现某个接口
    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) { /*...*/ }
    
        override fun mouseEntered(e: MouseEvent) { /*...*/ }
    })
    
    // 对象表达式 -> 实现抽象类
    open class A(x: Int) {
        public open val y: Int = x
    }
    interface B { /*...*/ }
    
    val ab: A = object : A(1), B {
        override val y = 15
    }
    
    // package 仅代表当前文件所属的 package 属性
    // 与 java 相比,其package无需与文件的实际路径相关,但是建议保持一致。
    package com.unluck.ktdemo.bean  // file://com/unluck/ktdemo/data
    
    // 同 java 类似,从当前函数返回
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return // non-local return directly to the caller of foo() 
            print(it)
        }
        println("this point is unreachable")
    } // result -> 12
    
    // 在 forEach 中使用 labels 则类似 continue , 其实 return 的是执行的 lamda 
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach lit@{
            if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop
            print(it)
        }
        print(" done with explicit label")
    } // result -> 1245 done with explicit label
    
    
    // 调用超类方法
    class Circle() : Shape() {
         override fun draw() {
            super.draw()
            println("Circle")
        }
    }
    // 调用超类构造
    class DemoView : View {
        constructor(ctx: Context) : super(ctx)
        constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
    }
    
    throw NoBugException()
    
    val bool : Boolean = true
    
    try {
        ...
    } catch ( e : Exception) {}
    
    class A {
        inner class Inner
    }
    class B {
        inner class Inner
    }
    
    typealias AInner = A.Inner
    typealias BInner = B.Inner
    
    • typeof 保留关键字,暂时没用,可能以后会用.

    • val 声明不可变变量 (只读) 属性 或者 局部变量.

    val finalValue : Int = 1
    
    var variable : Int = 1
    
    when {
        a == b -> {}
        else -> {}
    }
    
    when(a) {
        0 -> {}
        else -> {}
    }
    
    while (true) {
        ...
    }
    

    软关键字 Soft Keywords

    Kotlin 官网文档
    Soft Keywords
    https://kotlinlang.org/docs/keyword-reference.html#soft-keywords

    下面的这些符号在其适用的上下文中充当关键字,并且可以在其他上下文中用作标识符。

    interface Printer {
        fun print()
    }
    
    class DefaultPrinter : Printer {
        override fun print() {
            print("default printer")
        }
    }
    
    class DemoFragment : Fragment(), Printer by DefaultPrinter()  { // 委托实现接口
        val vm : ViewModel by viewModels() // 委托属性
    
        fun doSomePrint() {
            print() // print -> default printer
        }
    }
    
    try {
        ...
        throw NoBugException("Yes , NO BUG !!! ")
        ...
    } catch (e : NoBugException) {
        print("NO BUG ~ ")
    } catch (e2 : Exception) {
        print("F..k !!!! ")
    }
    
    class DemoView : View {
        constructor(ctx: Context) : super(ctx) {
            ...
        }
        constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
    }
    
    try {
        ...
    } finally {
        // optional finally block
    } 
    
    val isEmpty: Boolean
        get() = this.size == 0
    
    import com.unluck.ktdemo.Demo
    
    class Demo {
        init {
            print("init block")
        }
    }
    
    var setterVisibility: String = "abc"
        private set // the setter is private and has the default implementation
    
    var setterWithAnnotation: Any? = null
        @Inject set // annotate the setter with Inject
    
    // To declare an inline class, use the value modifier before the name of the class:
    value class Password(private val s: String)
    
    fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
        where T : CharSequence,
              T : Comparable<T> {
        return list.filter { it > threshold }.map { it.toString() }
    }
    

    修饰符关键字 Modifier keywords

    Kotlin 官网文档
    Modifier keywords
    https://kotlinlang.org/docs/keyword-reference.html#modifier-keywords

    以下符号作为声明中修饰符列表中的关键字,并可用作其他上下文中的标识符。

    @SinceKotlin("1.1") public actual typealias RandomAccess = java.util.RandomAccess
    
    @SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
    @SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
    @SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>
    @SinceKotlin("1.1") public actual typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
    @SinceKotlin("1.1") public actual typealias HashSet<E> = java.util.HashSet<E>
    
    @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
            AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
            AnnotationTarget.EXPRESSION)
    @Retention(AnnotationRetention.SOURCE)
    @MustBeDocumented
    annotation class Fancy
    
    class DemoClass {
        companion object Factory {
            fun create(): DemoClass = DemoClass()
        }
    }
    
    const val STATIC_FINAL_STRING: String = "public static final String"
    
    public inline fun <T, R : Comparable<R>> Iterable<T>.sortedBy(crossinline selector: (T) -> R?): List<T> {
        return sortedWith(compareBy(selector))
    }
    
    // 编译器会自动为其主构造函数中的属性生成 equals ,hashCode,toString,copy等方法
    data class DataBean (
        val name : String = "",
        val age  : Int = 0,
        var nick : String = "",
    ) : Serializable
    
    enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
    }
    
    • expect 将一个声明标记为平台相关, 并期待在平台模块中实现.

    • external 将一个声明标记为不是在 Kotlin 中实现(通过 JNI访问或者在JavaScript中实现).

    • final 禁止 重写方法.

    open class Rectangle() : Shape() {
        final override fun draw() { /*...*/ }
    }
    
    @file:kotlin.jvm.JvmName("TuplesKt")
    package kotlin
    public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
    
    // use to 
    val pair : Pair<String,String> = "A" to "B"
    
    @file:kotlin.jvm.JvmMultifileClass
    @file:kotlin.jvm.JvmName("MapsKt")
    @file:OptIn(kotlin.experimental.ExperimentalTypeInference::class)
    
    package kotlin.collections
    
    @SinceKotlin("1.1")
    @kotlin.internal.InlineOnly
    public inline fun  hashMapOf(): HashMap = HashMap()
    
    • inner 标识 内部类 会持有外部引用.
    // kotlin 默认非声明 inner 的 class 为静态内部类
    // 因此如需持有外部引用,需要单独添加 inner 标识
    class Outer {
        private val outValue: Int = 1
        inner class Inner {
            fun print() = outValue
        }
    }
    
    val print = Outer().Inner().print() // print == 1
    
    package kotlin.collections
    
    expect interface RandomAccess
    
    ...
    
    internal expect fun copyToArrayImpl(collection: Collection<*>): Array<Any?>
    
    internal expect fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<T>): Array<T>
    
    internal expect fun <T> arrayOfNulls(reference: Array<T>, size: Int): Array<T>
    internal expect fun <K, V> Map<K, V>.toSingletonMapOrSelf(): Map<K, V>
    internal expect fun <K, V> Map<out K, V>.toSingletonMap(): Map<K, V>
    internal expect fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?>
    
    class DemoClass {
        lateinit var lateValue : DemoBean
    
        fun doLateInit() {
            if (!this::lateValue.isInitialized) {
                lateValue = DemoBean()
            }
        }
    }
    
    open class Person {
        open fun age() = 10
    }
    class OldMan : Person() {
        override fun age() = 100
    }
    
    • operator 将方法声明为 重载运算符 或 约定运算.
      Kotlin 约定了一些函数规则,与之对应的有一系列运算符或者其它关键字约定。
      详细可参考官网对应文档,这里不再一一指出。

    • out 标记参数类型为 协变.
      表示告诉编译器,你只操作数据,并不变更消费数据,不关心其类型。

    public data class Pair<out A, out B>(
        public val first: A,
        public val second: B
    ) : Serializable
    
    open class Person {
        open fun age() = 10
    }
    class OldMan : Person() {
        override fun age() = 100
    }
    
    @MainThread
    inline fun <reified VM : ViewModel> Fragment.viewModels(
        noinline ownerProducer: () -> ViewModelStoreOwner = { this },
        noinline factoryProducer: (() -> Factory)? = null
    ) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
    
    • sealed 声明一个 密封类 (限制其继承关系).

    • suspend 标识一个方法或者 lamda 可被挂起 (在 协程 中使用).

    public suspend fun delay(timeMillis: Long) {
        if (timeMillis <= 0) return // don't delay
        return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
            cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
        }
    }
    
    // mutableListOf
    public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
        if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
    
    // vararg
    val list = mutableListOf(1,2,3,4)
    
    

    特殊标识符 Special identifiers

    Kotlin 官网文档
    Modifier keywords
    https://kotlinlang.org/docs/keyword-reference.html#special-identifiers

    以下标识符由编译器在指定上下文中定义,并且可以用作其他上下文中的常规标识符。

    • field 用在属性访问器内部来引用该 属性.
    var counter = 0 // the initializer assigns the backing field directly
        set(value) {
            if (value >= 0)
                field = value  
                // 不可以使用 counter = value ,否则会导致递归调用set ,ERROR StackOverflow
                // ERROR StackOverflow: Using actual name 'counter' would make setter recursive
        }
    
    // 隐式引用
    (1..100).random().takeIf { 
        it > 50  // it -> random 得到的 value 
    }?.let { 
        // it -> takeIf 函数的 返回值 ,
        print("random num $it")  
    }
    
    // 逻辑较多时 或 有嵌套 lamda,还是建议显示的声明其参数
    (1..100).random().takeIf { randomNum ->
        randomNum > 50 
    }?.let { num ->
        print("random num $num ")  
    }
    

    操作符 Operators and special symbols

    Kotlin 官网文档
    Operators and special symbols
    https://kotlinlang.org/docs/keyword-reference.html#operators-and-special-symbols

    Kotlin 以下操作符及部分特殊符号的定义

    • +, -, *, /, % 常见的数学操作符
    // mathematical operators
    val plus = 1 + 2
    val minus = 2 - 1
    val times = 2 * 2
    val div = 2 / 2
    val rem = 1 % 2
    
    // 展开运算符
    fun foo(arr : Array<String>) {
        val list = listOf("vararg params", *arr) // 展开运算符 展开数组内容
    }
    
    // 赋值
    val key : String = "key"
    
    // 默认参数
    fun hash(key : String = "") {}
    
    var num = 1
    num += 1  // num = 1 + 1 = 2 
    num -= 1  // num = 2 - 1 = 1
    num *= 8  // num = 1 * 8 = 8
    num /= 2  // num = 8 / 2 = 4
    num %= 3  // num = 4 % 3 = 1
    
    
    a++
    ++a
    a--
    --a
    
    • &&, ||, ! 逻辑“与”、“或”、“非”操作符(对于位运算,请使用相应的中缀函数) .
    if (nameStr.isNullOrEmpty() || psd.isNullOrBlank()) {
        ...
    }
    
    • ==, != 相等和不等运算符 (非基本类型会直接调用 equals() ).
      Float 和 Double 比较特殊,比如,如果当操作数不是静态类型化为浮点数,调用的就会是对应的equals方法和compareTo方法,会导致以下结果出现。
      a). -0.0 比 0.0小。
      b). NaN被认为与自身相等。
      c). NaN比无穷大都大。
    val a = DemoBean()
    val b = DemoBean()
    a == b // -> a?.equals(b) ?: (b === null)
    a != b // -> !(a?.equals(b) ?: (b === null))
    
    // Float or Double 
    // 静态化浮点数
    print(-0.0f == 0.0f) // true
    print(Float.NaN == Float.NaN) // false 
    print(Float.NaN > Float.POSITIVE_INFINITY) // false 
    
    // 特殊情况。 当通过非基本类型比较时,会出现以下结果
    // 非静态化为浮点数 例如 :Any
    val numZero1 : Any = -0.0f
    val numZero2 : Any = 0.0f
    println(numZero1 == numZero2) // false 通过 equals 比较
    
    // NaN
    val firstNaN : Any = Float.NaN
    val secondNaN = Float.NaN
    println(firstNaN == secondNaN) // true 通过 equals 比较
    
    a === b
    a !== b
    
    • <, >, <=, >= 比较运算符 (非基本类型会调用 compareTo() ).
      Float 和 Double 比较特殊,比如,如果当操作数不是静态类型化为浮点数,调用的就会是对应的equals方法和compareTo方法,会导致以下结果出现。
      a). -0.0 比 0.0小。
      b). NaN被认为与自身相等。
      c). NaN比无穷大都大。
    a > b  // a.compareTo(b) > 0
    a < b  // a.compareTo(b) < 0
    a >= b // a.compareTo(b) >= 0
    a <= b // a.compareTo(b) <= 0
    
    // Float 和 Double 存在特殊情况
    // 静态化浮点数
    print(-0.0f == 0.0f) // true
    print(Float.NaN == Float.NaN) // false 
    print(Float.NaN > Float.POSITIVE_INFINITY) // false 
    
    // Comparable
    val numZero3 : Comparable<Float> = -0.0f
    val numZero4 : Float = 0.0f
    println(numZero1 < numZero2)  // true 通过 compareTo 比较
    
    // 无穷
    val compareNaN : Comparable<Float> = Float.NaN
    val positiveInfinity = Float.POSITIVE_INFINITY // 正无穷
    println(compareNaN  > positiveInfinity)  // true 通过 compareTo 比较
    
    val item = array[i]
    array[i] = value
    
    val value = hashMap[key]
    hashMap[key] = value
    
    fun getNum() : Int? {
          return (1..10).random().takeIf { it > 5} // 返回大于 5 的 Int,否则 返回 null
    }
    val nonNullValue : Int = getNum()!!  // 如果 getNum() 返回 null ,则直接抛 NullPointerException 
    
    
    fun getNum() : Int? {
          return (1..10).random().takeIf { it > 5} // 返回大于 5 的 Int,否则 返回 null
    }
    val num : Int? = getNum()
    num?.plus(1) 
    
    fun getNum() : Int? {
          return (1..10).random().takeIf { it > 5} // 返回大于 5 的 Int,否则 返回 null
    }
    val num : Int = getNum() ?: 1  // getNum 为空时,num 等于 右侧的 1
    
    // member
    fun isOdd(x: Int) = x % 2 != 0
    
    val numbers = listOf(1, 2, 3)
    println(numbers.filter(::isOdd))  // ::isOdd ==> 代表isOdd方法,类型为 block(x) -> Boolean
    
    // class
    Intent(this@MainActivity,SecondActivity::class.java)
    
    1..3   // 1,2,3
    1..100 // 1,2,3 ... 98,99,100
    
    • : 分隔声明中的名称与类型.
    val name : String = ""
    
    val name : String? = null
    
    // lamda
    view.setOnClickListener { view ->
        print("view click")
    }
    v.setOnTouchListener { view, event -> 
        print("view touch event ${event.action}")
        false
    }
    
    // 函数类型
    fun check(block : (Boolean) -> Unit) {
        block(true)
        block.invoke(false)
    }
    check {
        print(it)
    }
    
    // when
    when(value) {
        0 -> {}
        else -> {}
    }
    
    // 注解
    @Route(path = "/user/center")
    class UserCenterActivity 
    
    // loop label
    breakToThis@ for (i in 1..100) {
        for (j in 1..100) {
            if (...) break@breakToThis // 终止 breakToThis@ for(i)
        }
    }
    
    // lamda label
    fun foo() {
        run loop@{
            listOf(1, 2, 3, 4, 5).forEach {
                if (it == 3) return@loop // non-local return from the lambda passed to run
                print(it)
            }
        }
        print(" done with nested loop")
    }
    
    // this
    class MainActivity .. {
        inner class Factory {
            fun create() {
                Intent(this@MainActivity,SecondActivity::class.java)
            }
        }
    }
    
    // super class
    class MainActivity : FragmentActivity... {
        inner class Factory {
            fun create() {
                val intent = Intent(this@MainActivity,SecondActivity::class.java)
                super@MainActivity.startActivity(intent)
            }
        }
    }
    
    • ; 分隔位于同一行的多个语句.
    val v = View(context); val t = Timer()
    
    print("num $num ")
    print("other ${other.num} ")
    
    • _ 未使用的参数,可以用该符号替代

    // lamda
    v.setOnTouchListener { _, _ -> 
        // fix all event
        true
    }
    
    // 解构
    val (_, status) = getResult()
    

    如果要查看各个运算符的优先级,可以查看 这篇官方文档 .

    参考文献

    Kotlin 官网文档
    Keywords
    https://kotlinlang.org/docs/keyword-reference.html

  • 相关阅读:
    1017 Queueing at Bank
    Java项目实战记录:雷达数据插值
    mplus使用注意
    【Java】poi-tl实现导出Word模板并动态渲染数据
    Putty中运行matlab文件
    pytest(4)-测试用例执行顺序
    Golang 串口通信
    什么是运放的虚短和虚断
    redis(7):Redis的特殊数据类型以及常用相关命令详解
    Docker 镜像上传到私有云和阿里云的超详细图文步骤
  • 原文地址:https://blog.csdn.net/CrazyApes/article/details/126941878