kotlin进阶学习记录

1,991 阅读4分钟

前言

使用kotlin已经有一段时间了,但是看一些开源项目总觉得自己只是学了点皮毛。正好组内还没人熟悉kotlin进阶相关知识,因此也是决定以此文来做个分享,开阔下技术视野

操作符

双引号::

Kotlin 中 双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。

 println(::methon2)
 fun methon2() {
    println("methon2")
}
结果:
fun methon2(): kotlin.Unit

函数

可变数量的参数(Varargs)

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


fun main(args: Array<String>) {
    hello(1,2,string = "hello")
    hello(1,2,3,string = "world")
    val asList = asList("2", "3")
    println(asList.toString())
}
//这里并不知道有几个int的  vararg表示变长参数
fun hello(vararg intParameter:Int,string: String){
    for (i in intParameter) {
        println(i)
    }
    println(string)
}

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}
结果:
1
2
hello
1
2
3
world
[2, 3]

lambda表达式

也可称为闭包,本质是匿名函数,这里从最基础的点击事件讲起

lambda结构

{ 参数1: 类型, 参数2: 类型  -> 表达式 }

lambda特点

  • lambda表达式总是被大括号括着
  • 其参数(如果存在)在 -> 之前声明(参数类型可以省略)
  • 函数体(如果存在)在 -> 后面。

lambda写点击事件

功能:点击隐藏该控件
    // java代码:
    tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                v.setVisibility(View.GONE);
            }
        });
       
      // kotlin 代码:
      
      // 1、kotlin 最原始写法
        tv.setOnClickListener(object : View.OnClickListener{
            override fun onClick(v: View) {
                v.visibility=View.GONE
            }
        })

        // 2、匿名函数改成lambda写法
        tv.setOnClickListener({ v:View -> v.visibility=View.GONE })

        // 3、当lambda是函数的唯一实参,就可以去掉空的小括号对
        tv.setOnClickListener { v:View -> v.visibility=View.GONE }

        //4、如果lambda的参数的类型可以被编译器推导出来,就可以省略它
        tv.setOnClickListener { v -> v.visibility=View.GONE }

        // 5、如果lambda只有一个参数,那么这个参数也可以省略掉。代码中引用这个参数的地方可以通过编译器自动生成的名称it来替代
        tv.setOnClickListener { it.visibility=View.GONE}      

闭包

闭包的作用

  • 让函数成为编程语言中的一等公民(一般来说,如果某程序设计语言中的一个值可以作为参数传递,可以从子程序中返回,可以赋值给变量,就称它为一等公民)
  • 让函数具有对象所有的能力(封装)
  • 让函数具有状态
 定义一个比较测试闭包
val test = if (5 > 3) {
    println("yes")
} else {
    println("no")
}

  methon1 { methon2() }
  fun methon1(body: () -> Unit) {
    body()
}
运行结果:
fun methon2(): kotlin.Unit
methon2

fun main(args: Array<String>) {
    val method = makeFun()
    method()
    method()
    method()
}

// 函数返回值是一个lambda表达式
fun makeFun(): ()->Unit{
    var count = 0
    return fun(){
        println(++count)
    }
}
// 运行结果
1
2
3

面向对象

方法重载与默认参数

扩展函数

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

 print("100".appendStr("%"))
 
/**
 * this 就是.前面的String,即这里是100
 * @receiver String
 * @param string String
 * @return String
 */
fun String.appendStr(string: String):String{
    return this+string
}
运行结果:
100%

在Android中通常可以写在BaseActivity/BaseFragment中,相当于给Activity、Fragment增加新功能

open class BaseActivity : Activity() {
    fun Context.toast(msg: String) {
        if (TextUtils.isEmpty(msg)) {
            return
        }
        Toast.makeText(this, msg, Toast.LENGTH_SHORT)
    }
}
实现类调用
toast("这波操作666")

属性代理

延迟属性 Lazy

lazy() 方法接收一个lamda作为参数并返回一个 Lazy实例, 可以实现延迟加载: 第一次调用 get()时执行传入 lazy()的lambda表达式并保存结果, 后续对get()的调用只返回保存的结果.

监控属性 Observable

Delegates.observable()有两个参数: 初始值和变化观察器. 每次代理属性被赋予值的时候都会调用观察器(在赋值操作之后).观察器有三个参数:属性类型, 旧值和新值.

自定义属性代理

定义方法:
val/var <property name>:<Type> by <expression>
代理者需要实现相应的getValue/setValue方法

class AttrDelegate{
    // val,不可变的,第一次访问name才赋值
    val name by lazy {
        println("这句话只会打印一次")
        "888"
    }

    val age by MyDelegate()
    var age2 by MyDelegate()
}
fun main(args: Array<String>) {
    var attrDelegate=AttrDelegate()
    println(attrDelegate.name)
    println(attrDelegate.name)
    println(attrDelegate.age)
    println(attrDelegate.age2)
    attrDelegate.age2="male"
    println(attrDelegate.age2)

}
//  为什么不是继承lazy
class MyDelegate{ //重载操作符的函数需要用 operator 修饰符标记。
    private var value: String? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValue: $thisRef -> ${property.name}")
        return value?: ""
    }

    /**
     * 要代理var的属性,除了实现getValue之外还需要再实现setValue
     */
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
        println("setValue, $thisRef -> ${property.name} = $value")
        this.value = value
    }
}
运行结果:
这句话只会打印一次
888
888
getValue: com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age

getValue: com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age2

setValue, com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age2 = male
getValue: com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age2
male

密封类

密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。

声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。

sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)

sealed class Student{
    abstract fun speak()

    class Boy:Student() {
        override fun speak() {

        }
    }

    class Girl:Student() {
        override fun speak() {
        }
    }

}

高阶函数

概念:高阶函数是将函数用作参数或返回值的函数。

函数类型

  • 所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(A, B) -> C 表示接受类型分别为 A 与 B 两个参数并返回一个 C 类型值的函数类型。

infix函数

infix函数必须满足以下要求

  • 它们必须是成员函数或扩展函数;
  • 它们必须只有一个参数;
  • 其参数不得接受可变数量的参数且不能有默认值。
println(1.add(2))
println(1 add 2) 
infix fun Int.add(x: Int): Int {
    return this + x
}
运行结果:
3
3

函数复合

概念:f(g(x))

/**定义两个函数*/
val add5 = {i: Int -> i + 5} //加5
val multiplyBy2 = {i: Int -> i * 2}  //乘2

fun main(args: Array<String>) {
//    println(multiplyBy2(add5(8)))  //(5 + 8) * 2
//
//    val add5AndMultiplyBy2 = add5 andThen multiplyBy2
//    val add5ComposeMutiplyBy2 = add5 compose multiplyBy2
//    println(add5AndMultiplyBy2(8))  //m(x)= f(g(x))    (8+5)*2
//    println(add5ComposeMutiplyBy2(8)) //m(x) = g(f(x))   8*2+5

    val add5AndMultiplyBy2 = add5.andThen(multiplyBy2)
    val add5ComposeMutiplyBy2 = add5.compose(multiplyBy2)
    println(add5AndMultiplyBy2(8))
    println(add5ComposeMutiplyBy2(8))
}

/**定义一个复合函数*/
/**
 * p1、p2是参数
 * R是返回值
 * andThen扩展函数
 * 参数:Function1<P2,P2>,第一参数是参数类型,第二个参数是返回值类型
 * 返回值:Function1<P1,R>
 * infix中缀表达式
 */
infix fun <P1,P2,R> Function1<P1,P2>.andThen(function: Function1<P2,R>): Function1<P1,R>{
    //进行复合
    //返回了一个函数
    return fun (p1: P1):R{
        //函数里面function.invoke把这个function又调用了一遍
        //然后又把自己的返回值传给了上去
        return function.invoke(this.invoke(p1))
    }
}

infix fun <P1,P2,R> Function1<P2,R>.compose(function: Function1<P1,P2>):Function1<P1,R>{
    return fun (p1: P1):R{
        return this.invoke(function.invoke(p1))
    }
}

运行结果:
26
21

反射

Kotlin文件中用java思维反射Kotlin代码

 class Person( var name: String, var age: Int){
    private fun eat(){
        println("eat")
    }
    fun speak(string: String){
        println(string)
    }
    override fun toString(): String {
        return "Person(name='$name', age=$age)"
    }
}

    val clazz = Class.forName("com.study.reflections.Person")
    val newInstance = clazz.getConstructor(String::class.java,Int::class.java).newInstance("zhangsan",10)
    println(newInstance)
    val method = newInstance.javaClass.getDeclaredMethod("eat").apply { isAccessible=true }
    val method2 = newInstance.javaClass.getDeclaredMethod("speak", String::class.java)
    method.invoke(newInstance)
    method2.invoke(newInstance,"说个666吧")
    val newInstance = clazz.getConstructor(String::class.java,Int::class.java).newInstance("zhangsan",10)
    println(newInstance)
    val method = newInstance.javaClass.getDeclaredMethod("eat").apply { isAccessible=true }
    val method2 = newInstance.javaClass.getDeclaredMethod("speak", String::class.java)
    method.invoke(newInstance)
    method2.invoke(newInstance,"说个666吧")
运行结果:
Person(name='zhangsan', age=10)
eat
说个666吧