美文网首页MVVM + kotlin+ jetpack
4 .遥遥领先 Android kotlin高级用法,高级函数,

4 .遥遥领先 Android kotlin高级用法,高级函数,

作者: 鹏城十八少 | 来源:发表于2024-04-01 11:48 被阅读0次
    封面.jpg

    目录

    1. Lameda

    1.1 Lamebda演变过程
    1.2 Lambda语法
    1.3 、lambda中下划线(_)
    1.4 Lambda和高阶函数的关系
    1.5 Lambda的底层原理:
    1.6 Lambda和闭包的关系

    2.kotlin的核心是啥? 高阶函数
    3.扩展函数

    3.1 let、with、apply区别是使用

    4.内敛函数inline
    5 .匿名函数和顶层函数
    6.泛型 ,使用Kotlin Reified 让泛型更简单安全
    7.DSL-Anko布局 高深,看不懂
    8.Kotlin系列之顶层函数和属性

    1. Lambda介绍

    1.1 Lamebda演变过程

    这里举例一个Android中最常见的按钮点击事件的例子

     gl_surface_view.setOnClickListener(View.OnClickListener {
        
        
    })
    

    简化;

    view.setOnClickListener { v: View ->
      switchToNextPage()
    }
    

    另外,如果这个 Lambda 是单参数的,它的这个参数也省略掉不写:
    简化成: ()-> 都省了!

    gl_surface_view.setOnClickListener {
       
    }
    

    1.2 Lambda语法

    有多个参数不能用it、自己定义
    当另外一个参数可以用下划线表示_
    为了让大家彻底的弄明白Lambda语法,我这里用三种用法来讲解。并且举例为大家说明

    箭头后面是返回值 : -> 返回值

    语法如下:

       1). 无参数的情况 :
        val/var 变量名 = { 操作的代码 }
    
        2). 有参数的情况
        val/var 变量名 : (参数的类型,参数类型,...) -> 返回值类型 = {参数1,参数2,... -> 操作参数的代码 }
    
        可等价于
        // 此种写法:即表达式的返回值类型会根据操作的代码自推导出来。
        val/var 变量名 = { 参数1 : 类型,参数2 : 类型, ... -> 操作参数的代码 }
    
        3). lambda表达式作为函数中的参数的时候,这里举一个例子:
        fun test(a : Int, 参数名 : (参数1 : 类型,参数2 : 类型, ... ) -> 表达式返回类型){
            ...
        }
    

    实战分析:

    //给传参数命名, 2个参数, 要自己写名字aNum,bNum
    var method5:(Int,Int)->Unit={aNum,bNum->
        Log.d("dd","updateGrade: $aNum","+$bNum")
    }
    
    //屏蔽传参, 下划线占位
    var method6:(Int,Int)->Unit={aNum,_ ->
        Log.d("dd","updateGrade: $aNum","+$aNum")
    }
    
    //传入值和返回值(高级的写法)
    var method07:(String) ->String ={str->str}
    
    //省去返回值
    val method2={ Log.d("dd","updateGrade:")}
    
    //一个参数默认是it
    var method3:(String) ->Unit={
        Log.d("dd",":$it")
    }
    
    var method4:(Int)->Unit={
        when(it){
            1->  Log.d("dd",":$it")
            else ->Log.d("dd",":$it")
        }
    }
    

    经过上面的实例讲解与语法的介绍,我们对其作出一个总结:

    1).lambda表达式总是被大括号括着。
    2). 定义完整的Lambda表达式如上面实例中的语法2,它有其完整的参数类型标注,与表达式返回值。当我们把一些类型标注省略的情况下,

    就如上面实例中的语法2的另外一种类型。当它推断出的返回值类型不为'Unit'时,它的返回值即为->符号后面代码的最后一个(或只有一个)表达式
    的类型。

    3). 在上面例子中语法3的情况表示为:高阶函数,当Lambda表达式作为其一个参数时,只为其表达式提供了参数类型与返回类型,

    所以,我们在调用此高阶函数的时候我们要为该Lambda表达式写出它的具体实现。
    invoke()函数:表示为通过函数变量调用自身,因为上面例子中的变量b是一个匿名函数。
    it
    it并不是Kotlin中的一个关键字(保留字)。
    it是在当一个高阶函数中Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的。

    1.3 、lambda中下划线(_)

    在使用Lambda表达式的时候,可以用下划线(_)表示未使用的参数,表示不处理这个参数。

    同时在遍历一个Map集合的时候,这当非常有用。

    举例:

    val map = mapOf("key1" to "value1","key2" to "value2","key3" to "value3")
    
    map.forEach{
         key , value -> println("$key \t $value")
    }
    
    // 不需要key的时候
    map.forEach{
        _ , value -> println("$value")
    }
    
    
    输出结果:
    key1     value1
    key2     value2
    key3     value3
    value1
    value2
    value3
    

    1.4 Lambda和高阶函数的关系

    函数里面传入一个lambda表达式是什么意思?
    举例: 登录

     fun loginAction(userName:String,userPwd:String, loginResponseResult:(Boolean)->Unit){
    
        if(userName==null){
           return
        }
        loginEngine(userName,userPwd,loginResponseResult)
    
    }
    

    Lambda表达式是为我们减少了大量的代码,但是Lambda表达式是为后面的高阶函数章节打下基础
    在 Java 里是不允许把方法作为参数传递的,但是我们有一个历史悠久的变通方案:接口把方法当成参数传递到参数

    1.4.1 双冒号 ::method 到底是什么?

    解释:
    1). 一个函数名的左边加上双冒号,它就不表示这个函数本身了,而表示一个对象
    2).通过变量, 把函数赋值给变量

    @JvmStatic
    fun startActivity(context: Context, bean: SingleTrainBean) {
        val intent = Intent(context, AICoachSingleTrainActivity::class.java)
        intent.putExtra(SINGLE_TRAIN_BEAN, bean)
        context.startActivity(intent)
    }
    

    这个双冒号的写法叫做函数引用 Function Reference
    因为加了两个冒号,这个函数才变成了一个【对象】。这样就可以赋值给左边的变量!

    fun b(param: Int): String {
      return param.toString()
    }
    val d = ::b
    val e = d
    

    创建一个函数类型的对象有三种方式:双冒号加函数名、匿名函数和 Lambda;
    Lambda 和匿名函数的联系!
    匿名函数:没有函数名

    a(fun b(param: Int): String {
      return param.toString()
    });
    val d = fun b(param: Int): String {
      return param.toString()
    }
    

    另外,这种写法的话,函数的名字其实就没用了,所以你可以把它省掉:

    a(fun(param: Int): String {
      return param.toString()
    });
    val d = fun(param: Int): String {
      return param.toString()
    }
    

    Kotlin 的匿名函数和 Lambda 表达式的本质,它们都是函数类型的对象。
    因为对象才能当参数传递匿名函数虽然叫匿名函数,但是它不是函数,是函数类型的对象!

    1.5 Lambda的底层原理:

    Kotlin 的匿名函数和 Lambda 表达式的本质,它们都是函数类型的对象
    Lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。但是我们在用的时候不必关心起底层实现。不过Lambda的出现确实是减少了代码量的编写,同时也是代码变得更加简洁明了。
    原理:lameda表达式---->高阶函数--------->函数类型的对象------>匿名函数------(简化通过lemeda)----->lameda
    内部类: 内部类访问外部类的变量, 需要用final修饰, 不然会报错!

    面试官:kotlin的lameda表达式和java的lameda有啥区别?

    问题: Java有lambda表达式吗?

    Java 8 的 Lambda 只是一种便捷写法,本质上并没有功能上的突破,而 Kotlin 的 Lambda 是实实在在的对象。
    Java 8 Lambda 表达式来创建匿名类对象。lambda 表达式不生成匿名类文件,但它本质上还是在创建一个匿名类对象,只是一种简化写法而已

    Kotlin lambda 表达式完全通过生成匿名类实现,其它特点类似。 Kotlin 的 Lambda 还能当函数参数?

    支持动态传入函数,

    因为 Kotlin 的 Lambda 是实实在在的函数类型的对象

    1.6 Lambda和闭包的关系

    <pre style="margin: 8px 0px;">在 Java8 版本使用 lambda 表达式是有限制的,它不是真正意义上的闭包,而 Kotlin 中的 lambda 才是真正意义上支持闭包的实现。 </pre>

    2. 高阶函数

    kotlin的核心是啥? 高阶函数, 携程是扩展库

    Kotlin 函数是一级函数,这意味着它们可以存储在变量和数据结构中,作为参数传递给其他高阶函数,也可以从其他高阶函数返回。

    对于其他非函数值,你可以以任何可能的方式对函数进行操作。

    高阶函数定义-Heigher-Order-Functions

    • 高阶函数定义:参数类型是函数类型,或者返回值是函数类型的函数称为高阶函数,lambmda也算是一个函数, 除此之外函数类型还能赋值给变量

    源码: Functions.kt里面定义了函数

    高阶函数的作用: 可以动态执行哪个方法, 具体在 a 执行的时候内部调用哪个方法,我希望可以动态设置

    问题: java中是如何实现: 方法中调用方法?

    通过接口, 接口里面定义方法, 传入接口搞定!

    2.1 举例:登入:

        /**
         * 传入2个值,还有一个是内部得到的结果
         */
        loginAction("peng","cai"){
            if(it){
                println("suc")
            }else{
                println("fail")
            }
        }
    }
     fun loginAction(userName:String,userPwd:String, loginResponseResult:(Boolean)->Unit){
    
        if(userName==null){
           return
        }
        loginEngine(userName,userPwd,loginResponseResult)
    
    }
    
    /***
     * 传入3个值,最后一个赋值结果变量
     */
     fun loginEngine(userName:String,userPwd:String, loginResponseResult:(Boolean)->Unit){
    
        if("peng"==userName||userName=="cai"){
            loginResponseResult(true)
        }else{
            loginResponseResult(false)
        }
    }
    
    2.2 apply()高阶函数源码分析:
    2.3 内敛高阶函数:

    高阶函数:
    把lambda作为参数传递!

    () -> Unit //表示无参数无返回值的Lambda表达式类型
    
    (T) -> Unit //表示接收一个T类型参数,无返回值的Lambda表达式类型
    
    (T) -> R //表示接收一个T类型参数,返回一个R类型值的Lambda表达式类型
    
    (T, P) -> R //表示接收一个T类型和P类型的参数,返回一个R类型值的Lambda表达式类型
    
    (T, (P, Q) -> S) -> R //表示接收一个T类型参数和一个接收P、Q类型两个参数并返回一个S类型的值的Lambda表达式类型参数,返回一个R类型值的Lambda表达式类型
    

    3.扩展函数

    3.1 什么是扩展函数

    T.()是啥 T.()也是一个泛型扩展函数
    扩展函数: 不需要再添加类, 或者类中增加方法!
    Kotlin允许开发者在不改变已有类的情况下,为某个类添加新的函数。这个特性叫做扩展函数。
    你可以给已有的类去额外添加函数和属性,而且既不需要改源码也不需要写子类
    问题: 谁可以调用这个扩展函数?
    .前面的可以用, T类型的可以调用

    class Extensions {
    
      fun String.method1(i: Int) {
        ...
      }
    
      ...
    

    kotlin FunctionType: function references , lambda , 匿名函数

    扩展方法编译成java后,实际上是一个 public static的静态方法,传入了对象实例和参数

    扩展函数:
    为什么要使用Kotlin中的扩展函数?
    如何使用扩展函数和扩展属性?
    扩展函数和属性原理
    扩展函数和成员函数区别
    扩展函数不可以被重写

    3.2 Kotlin写一个let的扩展函数,扩展函数和扩展属性?Kotlin中的扩展函数本质原理是什么?
    kotlin的扩展方法是怎么实现的,inline关键字的作用
    因为只要反编译过Kotlin扩展函数的代码都知道回答
    扩展函数的缺点:  找不到! 
    

    3.3. 扩展函数的使用场景:

    3.3.1 比方说,把dp转成px
    val Float.dp
      get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this,
        Resources.getSystem().displayMetrics
      )
    
    ...
    
    val RADIUS = 200f.dp
    
    3.3.2 举例: Math.pow()

    顶层函数:所有地方都可以访问,就像局部变量.
    并且它不属于任何类,但是可以声明限制某个类才可以使用!
    举一个简单的例子。如果要关闭一个I/O流,使用Java可能是写一个工具方法。

    /**
     * 安全关闭io流
     * @param closeable
     */
    public static void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    对Kotlin而言,可以对Closeable扩展一个函数closeQuietly()。

    fun Closeable?.closeQuietly() {
        try {
            this?.close()
        } catch (e: Throwable) {
        }
    }
    

    之后,任何实现了Closeable接口的类,都可以使用它本身的closeQuietly()方法来关闭流。我们不再需要那个工具方法了。

    使用扩展函数

    fun loge(tag: String, content: String?) {
        Log.e(tag, content ?: tag)
    }
    

    3.3.3 项目中的实战:

    在项目中,我们使用扩展函数对Glide做了封装,大大简化了Glide的使用。

    /**
         * 占位符矩形
         */
        fun ImageView.load(url: String) {
            get(url).placeholder(R.drawable.shape_default_rec_bg)
                    .error(R.drawable.shape_default_rec_bg)
                    .into(this)
        }
    
        /**
         * 占位符圆角矩形
         */
        fun ImageView.loadRound(url: String) {
            get(url).placeholder(R.drawable.shape_default_round_bg)
                    .error(R.drawable.shape_default_round_bg)
    //            .apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0)))
                    .transform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0))
                    .into(this)
        }
    
        /**
         * 占位符圆形
         */
        fun ImageView.loadCircle(url:Drawable) {
            get(url).placeholder(R.drawable.shape_default_circle_bg)
                    .error(R.drawable.shape_default_circle_bg)
                    .into(this)
        }
    
        fun ImageView.loadCircle(url: String) {
            get(url).placeholder(R.drawable.shape_default_circle_bg)
                    .error(R.drawable.shape_default_circle_bg)
                    .into(this)
        }
    
        fun ImageView.get(url: String): GlideRequest<Drawable> = GlideApp.with(context).load(url)
        fun ImageView.get(url: Drawable): GlideRequest<Drawable> = GlideApp.with(context).load(url)
    fun Context.toast(str: String) = Toast.makeText(this, str, Toast.LENGTH_LONG).show()
    
    // 当我们需要toast的时候,我们可以这样调用
    
    context.toast("test")
    

    function type: 对应function这么一个类

    fun printChar2(str: String, block: String.() -> Char) { // 高阶函数变成函数的扩展
        println(str.block())
    }
    
    fun printChar1(str: String, block: (String) -> Char) { // string是入参, char是返回值
        println(block(str)) // 函数里面调用函数
    }
    
    
    fun String.lastChar(): Char { // 得到最后一个字符
        return this.get(this.length - 1)
    }
    
    fun main2() {
        val lastChar: (String) -> Char = String::lastChar
        printChar1("Kotlin") { // 如何传入函数,是在{}中传入的lastChar()函数
            lastChar(it)
        }
    }
    
    

    3.4 问题: Kotlin的高阶函数使用过吗(平时使用的map,let ,apply ,also之类的都是高阶函数),let、with、apply的适用场景和区别?

    说到扩展函数,就不得不提的几个基础库的扩展函数,也叫做作用域函数,它们是:

    T.run、T.let、T.also、T.apply 以及run、width两个函数。对于这几个函数的使用刚开始的时候我还是很懵逼的,只是大概了解用法,以及带来的链式调用的便捷

    let、with、apply、run、also这几个高阶函数非常相似

    let 经常和?.操作符号联合使用,替代IF判断,比如非空判断

    let 扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。

    apply 创建对象并初始化设置

    run run函数有两种:一个是全局函数、一个是扩展函数

    全局函数run 替代 java中 { } 代码作用域

    run 函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,即返回 lambda 表达式的返回值。

    let比较.jpg

    4.内敛函数inline

    @InlineOnly
    public inline fun <T> Continuation<T>.resume(value: T): Unit =
        resumeWith(Result.success(value))
    

    高阶函数有性能缺陷一般: 会把代码拷贝到引用的地方, 会增加很多代码! 主要是因为lameda, lameda的本质是函数类型的

    对象, 会产生多余的对象, 如果是循环里面创建, 就会影响性能
    inline: 打标记, 编译的时候, 可以把代码铺平, 不产生临时对象, 减少临时对象的创建!

    内敛函数, inline关键字的作用,知不知道编译后Java中如何表示

    Inline关键字修饰的函数是内联函数,用于优化函数调用的压栈操作,相当于在编译期间把内联函数的代码拷贝到函数调用处,可以提升性能,但是增加了代码量

    LiveData源码里面用到了inline
    noinline: 不内敛, 有啥用, 会把高阶函数的功能缩减! 主要作用: 本来可以拿到的对象, 现在获取不到了
    crossinline: return的使用的时候, 解除限制,

    总结下来就是:

    inline 可以让你用内联——也就是函数内容直插到调用处——的方式来优化代码结构,从而减少函数类型的对象的创建;
    noinline 是局部关掉这个优化,来摆脱 inline 带来的「不能把函数类型的参数当对象使用」的限制;
    crossinline 是局部加强这个优化,让内联函数里的函数类型的参数可以被当做对象使用。

    5 .匿名函数

    val d = fun(param: Int): String {
      return param.toString()
    }
    

    它没有名字 , 变成lambda程这样

    val b = { param: Int -&gt;
      return param.toString()
    }
    

    另外 Lambda 的返回值不是用 return 来返回,而是直接取最后一行代码的值:
    匿名函数的本质:
    因为 Kotlin 的匿名函数不——是——函——数。它是个对象. (匿名函数不是函数)

    6. 解释一下泛型的逆变和协变?

    泛型:in和out *
    限制集合和数组不一样
    ?extend 上界 用来解除限制------->, out 使用 extend通配符表示可以读,不能写
    ? super 下界------------------------------> in。 使用 super 通配符表示可以写,不能读
    一个代表消费,一个代表产出
    如果in操作符在if语句中,则表示判断含义
    Kotlin 中 out 相当于 <? extends T>,in 相当于 <? super T>

    public interface Continuation<in T> { // 
        /**
         * The context of the coroutine that corresponds to this continuation.
         */
        public val context: CoroutineContext
    
        /**
         * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
         * return value of the last suspension point.
         */
        public fun resumeWith(result: Result<T>)
    }
    

    intercepted扩展函数也是expect的,又得去kotlin仓库里面找jvm相关的实现
    // 接口.接口。 接口里面嵌套接口
    *代表的含义!

     override val key: CoroutineContext.Key<*> get() = Job    
    

    相关文章

      网友评论

        本文标题:4 .遥遥领先 Android kotlin高级用法,高级函数,

        本文链接:https://www.haomeiwen.com/subject/nncitjtx.html