• Kotlin 知识点记录


    1、Kotlin 之 var val

    在kotlin语法中,修饰符var用来修饰可变变量,val修饰只读变量。关于val的只读属性而不是不可变属性,可以参考:val的只读属性

    var name: String = "Mikyou"
    val age: Int = 18
    

    1、Kotlin定义一个变量和常量比Java更简洁和灵活
    2、Kotlin中拥有类型推断的机制,当一个变量或者常量初始化的时候可以省略类型的声明,它会根据初始化值的类型来作为变量的类型。
    3、Kotlin声明变量和常量都必须使用var(变量),val(常量)关键字开头,然后再是名称,最后才是类型(如果有初始化值类型可以直接省略)
    4、Kotlin相比Java默认的访问修饰符是public,而Java中是default
    5、Java中一般都是以类型开头然后再是名称,并且类型是不可以省略的;这样的操作在Kotlin中是不行的。因为在Kotlin中类型是可以省略的,也就是类型相对比较弱化的,所以Kotlin会把类型放在最后,一般含有初始化值就会把在后面的类型声明省略。

    Kotlin的自定义属性访问器

    在Kotlin中属性是头等特性,它习惯于用一个属性去替代Java中的字段和setter,getter方法。而Kotlin中的set、get访问器相当于Java中的setter,getter方法。Kotlin有个新的特性就是可以去定义一个类中属性的访问器包括setter,getter访问器。该特性十分适用于需要经过多个属性逻辑计算得出的一个新的属性。那么逻辑计算的过程操作不需要像Java一样开一个方法来实现。可以直接在属性访问器中进行逻辑运算。

    实例:

    1. 自定义get属性访问器
    class Rectangle(width: Float, height: Float) {
        val isSquare: Boolean//在Kotlin中只需要一个属性就能解决,重新定义了isSquare的getter属性访问器,访问器中可以写入具体逻辑代码。
            get() {
                return width == height
            }
    }
    
    1. 自定义set属性访问器
    // 测试
    fun main(args: Array) {
        val mime = RoundImageView()
    
        println("str = ${mime.str1}")
        mime.str1 = ""
        println("str = ${mime.str1}")
        mime.str1 = "kotlin"
        println("str = ${mime.str1}")
    
    }
    class RoundImageView  {
        var str1 = "test"
           get() = field   // 这句可以省略,kotlin默认实现的方式
            set(value){
                field = if (value.isNotEmpty()) value else "null"
            }
    }
    

    特性:

    • 使用了val修饰的属性,不能有setter().
    • 不管是val还是var修饰的属性,只要存在getter(),其值再也不会变化
    • 使用var修饰的属性,可以省略掉getter(),不然setter()毫无意义。当然get() = field除外。而get() =
      field是Koltin默认的实现,是可以省略这句代码的。
    2、const

    Kotlin同时又提供了一个const修饰符。在开发过程中,如果我们在伴生对象中定义了一个val类型的变量,那么Android Studio会智能的提示开发者需要使用const来修饰该变量。
    const的特点
    1、const只能修饰val,不能修饰var类型变量
    2、const 只允许在top-level级别和object(伴随对象也是obejct)中声明。

    const val constVariable = "const_variable"
    
    object obj{
        const val constVariable = "const_variable"
    }
    
    class ConstKotlin {
        companion object{
            const val constVariable = "const_variable"
        }
    }
    
    

    引用:
    (1)在kotlin语法中,有没有const修饰都是直接引用的,感觉不到引用方式上有什么区别。
    (2)在java语法中,没用const修饰的,通过getter方法获取属性信息,而用了const修饰符的属性,则可以直接像java中的常量一样直接通过类引用。
    (3)const val 修饰的属性相当于java中的public final static修饰的常量,可以通过类名直接访问。
    (4)val 修饰的属性相当于java中private final static修饰的常量,由于可见行为private,所以只能通过生成getter方法访问。
    (5)出于性能考虑,使用const val方式可以避免频繁函数调用。

    3、可变数量的参数(Varargs)

    函数的参数(通常是最后一个)可以用 vararg 修饰符标记:

    fun  asList(vararg ts: T): List {
        val result = ArrayList()
        for (t in ts) // ts is an Array
            result.add(t)
        return result
    }
    

    允许将可变数量的参数传递给函数:

    val list = asList(1, 2, 3)
    

    4、中缀表示法

    标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:

    • 它们必须是成员函数或扩展函数;
    • 它们必须只有一个参数;
    • 其参数不得接受可变数量的参数且不能有默认值。
    infix fun Int.shl(x: Int): Int { …… }
    
    // 用中缀表示法调用该函数
    1 shl 2
    
    // 等同于这样
    1.shl(2)
    

    5、扩展函数

    扩展函数简介:Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。 这通过叫做 扩展 的特殊声明完成。 例如,你可以为一个你不能修改的、来自第三方库中的类编写一个新的函数。 这个新增的函数就像那个原始类本来就有的函数一样,可以用普通的方法调用。 这种机制称为 扩展函数 。此外,也有 扩展属性 , 允许你为一个已经存在的类添加新的属性。

    声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。

    fun MutableList.swap(index1: Int, index2: Int) {
        val tmp = this[index1] // “this”对应该列表
        this[index1] = this[index2]//这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号前的对象)
        this[index2] = tmp
    }
    
    val list = mutableListOf(1, 2, 3)
    list.swap(0, 2) // “swap()”内部的“this”会保存“list”的值
    

    如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数与属性。就像伴生对象的常规成员一样, 可以只使用类名作为限定符来调用伴生对象的扩展成员

    6、高阶函数与 lambda 表达式

    函数字面值是指不声明,而直接作为表达式传递的函数。它包括两种,lambda表达式及匿名函数。
    字面量,就是不用变量名称直接用相对应的值写出来。比如“hello world”就是一个字符串字面量、12.23是一个 Double 的字面量、4是一个 Int 的字面量。

    Kotlin 函数都是头等的,这意味着它们可以存储在变量与数据结构中、作为参数传递给其他高阶函数以及从其他高阶函数返回。可以像操作任何其他非函数值一样操作函数。

    高阶函数是将函数用作参数或返回值的函数。
    Kotlin中的函数类型形如:()->Unit、(Int,Int)->String、Int.(String)->String等。它们有参数和返回值。
    最后一个Int.(String)->String比较奇怪,它表示函数类型可以有一个额外的接收者(提供接收者对象)类型。函数类型可以有一个额外的接收者类型,它在表示法中的点之前指定: 类型 A.(B) -> C 表示可以在 A 的接收者对象上以一个 B 类型参数来调用并返回一个 C 类型值的函数。 带有接收者的函数字面值通常与这些类型一起使用。
    挂起函数属于特殊种类的函数类型,它的表示法中有一个 suspend 修饰符 ,例如 suspend () -> Unit 或者 suspend A.(B) -> C

    函数类型实例化
    有几种方法可以获得函数类型的实例:

    使用函数字面值的代码块,采用以下形式之一:

    • lambda 表达式: { a, b -> a + b },
    • 匿名函数: fun(s: String): Int { return s.toIntOrNull() ?: 0 }
      带有接收者的函数字面值可用作带有接收者的函数类型的值。

    使用已有声明的可调用引用:
    顶层、局部、成员、扩展函数:::isOdd、 String::toInt,
    顶层、成员、扩展属性:List::size,
    构造函数:::Regex
    这包括指向特定实例成员的绑定的可调用引用:foo::toString。

    函数类型实例调用
    函数类型的值可以通过其 invoke(……) 操作符调用:f.invoke(x) 或者直接 f(x)。

    如果该值具有接收者类型,那么应该将接收者对象作为第一个参数传递。 调用带有接收者的函数类型值的另一个方式是在其前面加上接收者对象, 就好比该值是一个扩展函数:1.foo(2),

    val stringPlus: (String, String) -> String = String::plus
    val intPlus: Int.(Int) -> Int = Int::plus
    
    println(stringPlus.invoke("<-", "->"))
    println(stringPlus("Hello, ", "world!")) 
    
    println(intPlus.invoke(1, 1))
    println(intPlus(1, 2))
    println(2.intPlus(3)) // 类扩展调用
    
    Lambda 表达式与匿名函数

    lambda 表达式与匿名函数是“函数字面值”,即未声明的函数, 但立即做为表达式传递。
    完整语法:
    val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
    匿名函数:可以指定函数的返回类型
    语法:fun(x: Int, y: Int): Int = x + y

    Lambda 表达式或者匿名函数(以及局部函数和对象表达式) 可以访问其 闭包 ,即在外部作用域中声明的变量。

    var sum = 0
    ints.filter { it > 0 }.forEach {
        sum += it
    }
    print(sum)
    
    带有接收者的函数字面值

    带有接收者的函数类型,例如 A.(B) -> C,可以用特殊形式的函数字面值实例化—— 带有接收者的函数字面值。

    如上所述,Kotlin 提供了调用带有接收者(提供接收者对象)的函数类型实例的能力。

    在这样的函数字面值内部,传给调用的接收者对象成为隐式的this,以便访问接收者对象的成员而无需任何额外的限定符,亦可使用 this 表达式 访问接收者对象。

    这种行为与扩展函数类似,扩展函数也允许在函数体内部访问接收者对象的成员。

    https://www.kotlincn.net/docs/reference/lambdas.html
    https://www.jianshu.com/p/bccf115655a6

    7、Kotlin作用域函数详解

    参考文章

    作用域函数是Kotlin内置的,可对数据进行操作转换等 都是内联函数
    常见有 let run apply also with repeat

    let和run:

    • let和run都是扩展函数
    • let的Lambda有参数,该参数就是T,也就是待扩展的那个对象,所以可以在Lambda内访问该参数,从而访问该参数对象的内部公开属性和函数
    • run的Lambda没有参数,但这个Lambda是待扩展的那个对象T的扩展,这是带接收者的函数类型,所以可以看做这个Lambda是T的成员函数,直接调用该Lambda就是相当于直接调用该T对象的成员函数,所以在该Lambda内部可以通过this来访问T的公开属性和函数(只能访问公开的,稍后解释是为什么)。
    • let和run都是返回的Lambda的执行结果

    also和apply:

    • also和apply都是扩展函数
    • also和apply都是返回原对象本身,区别是apply没有Lambda参数,而also有
    • also的Lambda有参数,该参数就是T,也就是待扩展的那个对象,所以可以在Lambda内访问该参数,从而访问该参数对象的内部公开属性和函数
    • apply的Lambda没有参数,但这个Lambda是待扩展的那个对象T的扩展,这是带接收者的函数类型,所以可以看做这个Lambda是T的成员函数,直接调用该Lambda就是相当于直接调用该T对象的成员函数,所以在该Lambda内部可以通过this来访问T的公开属性和函数(只能访问公开的,稍后解释是为什么)

    repeat

    • repeat是一个顶层函数
    • 该函数有2个参数,一个是重复次数,另一个是需执行的Lambda,Lambda带参数,该参数表示第几次执行
    • 函数内部非常简单,就是一个for循环,执行Lambda

    with

    • with是一个顶层函数
    • with有2个参数,一个是接收者,一个是带接收者的函数
    • with的返回值就是block函数的返回值
    • block是T的扩展,所以可以使用receiver对象直接调用block函数,而且block内部可以使用this来访问T的公开属性和函数

    8、解构

    解构就是将一个对象中的参数拆开来,称为一个个单独的变量,从而使用这些变量进行单独的操作;
    1、通过kotlin的解构我们可以将一个对象拆解成为多个变量,对象的定义如下:

    class Goods(val name:String,val price:Double,val num : Int){
        operator fun component1() = name
        operator fun component2() = price
        operator fun component3() = num
    }
    
    

    对于对象的解构,我们必须使用operator fun component的方式来定义,后面的数字也是必须的,约定了解构时对应的顺序。

        //解构常用的用法1 将一个对象拆解成为多个变量
        val goods = Goods("辣条",1.0, 100)
        val (name,price,count) = goods
        //不需要的参数可以_代替
           val (_,_,count1)=goods
        println("${name}的单价是$price,目前仓库剩余${count}个")
    

    2、通过解构可以方便的遍历map

        //解构常用的用法2 循环遍历,遍历map
        val map = mapOf("key1" to "value1" , "key2" to "value2" , "key3" to "value3")
        for ((key,value) in map){
            println("key是$key,value是$value")
        }
    
    

    kotlin Any 与java object 区别
    1、都是顶级父类
    2、Any只有hashcode,equals,tostring 3个成员方法 object 11个成员方法
    3、Any是非空类,所以Any类型的变量不持有null值。如果你需要可以持有任何可能值得变量,包含null在内,则必须使用Any?类型。在底层,Any类型对应java.lang.Object,在Kotlin函数使用Any 时,它会编译成为Java字节码的Object。

    ::plus 函数引用方式的写法 表示把 plus()函数作为参数进行传递

    lambda 表达式在底层被转换为匿名类的实现方式,我们每调用一次表达式都会创建一个新的匿名类实例,造成额外的内存和性能开销。
    inline 内联函数:在编译器会将内联函数中的代码在进行编译的时候自动替换到调用它的地方,不存在运行时的开销
    noinline 非内联函数
    内联函数引用的lambda表达式可以使用return进行函数返回,非内联函数只能进行局部返回
    泛型
    泛型类
    class Myclass{
    }
    泛型方法
    class Myclass{
    fun eitt():T{
    }
    T:Number 指定泛型上界 默认上界是Any?
    }
    by lazy
    Kotlin 序列 Sequence
    协程 flow

    协程参考文章:https://www.jianshu.com/p/42464606fe08
    https://www.jianshu.com/p/301bacbda239

  • 相关阅读:
    LabVIEW实现变风量VAV终端干预PID控制
    力扣 -- 879. 盈利计划(二维费用的背包问题)
    如何自定义书写中间件?
    HTML的基础标签和HTML的Form表单
    nginx反向代理与负载均衡
    深入理解数组及其操作
    Java:升序数组插入一个元素,结果依旧是升序
    Go 互斥锁Mutex
    springcloud旅游网站源码
    Shell 脚本学习
  • 原文地址:https://blog.csdn.net/muranfei/article/details/127071057