Kotlin 用好用对高级特性分析笔记-持续更新

目录:

• 1.作用域函数 apply、also 与 let、run

• 2.委托 by 关键字

• 3.用when替代switch 与 if + N个else if

• 4.inline,noinline,crossinline

• 5.操作符重载、中缀表达式

• 6.拓展函数,拓展属性,匿名拓展

1 作用域函数 apply、also 与 let、run 使用场景建议

1.1 不需要返回其他类型对象 使用 apply、also,通常用于对象初始化时

image.png

image.png

1.2 需要返回其他类型对象 使用 let,run,通常用于对象引用时

image.png

image.png

2 委托 by 关键字

2.1 常用的属性委托 by lazy

维持一个单例成员,在首次访问时才创建,有点像java 中的懒加载

image.png

他们三种使用方式,效果都一样。在创建时都将使用悲观锁,锁对象都是声明成员的当前类,都会产生 [锁] 开销
实际使用过程中,需要慎重考虑2点,否则得不偿失

  • 1 是否需要延迟加载
  • 2 是否需要线程安全

2.2 自定义属性委托

场景分析:假设下面的两个变量都需要直接操作 set 与 get
这里写了两遍一样的样板代码,这能避免吗?这时候就用到了自定义属性委托

image.png

创建一个委托对象,实现 get set 方法 并在启动补充属性操作的逻辑

image.png

通过 by 关键字调用

image.png

这看似神奇但却不是什么黑魔法,都是依赖于编译器自动创建了相关代码逻辑,通过反编译就可以看到

image.png

2.3 想 by 点儿啥 就 by 点儿啥?官方文档也坑爹

image.png

这样看起来似乎挺爽的,这个例子中先不说类型安全的问题,最关键是我们用kotlin是为了消除NPE
但官网文档此处就是教开发者如何用kotlin特性不知不觉中写出一个NPE来

image.png

普通写法可以避免NPE吗?当然是可以的,编译器可以帮助你避免

image.png

2.4 使用委托编辑实现静态代理

image.png

3 用when替代switch 甚至是 if + N个else if

image.png

不过when 也容易when出坑

image.png

此处第一个条件实际编译后的代码是
BooleanCompanionObject.INSTANCE
这便是问题所在,这是非常危险的,也许永远都不达预期
注意: when 的条件前面不加 is 的时候就是判断具体的值

4 inline,noinline,crossinline

4.1 inline的主要作用是节省匿名函数带来的开销

  • 不使用 inline

image.png

  • 使用 inline

image.png

  • 降低了调用栈的深度

image.png

但是也会使代码变得膨胀(因为他会拷贝到所有调用出),具Google GDE所说,降低调用栈并不能有效的提升多少性能

总结来说是利用编译时常量的思想优化性能,比如变量使用 const (java中 static final ),函数则使用 inline

所以如果一个 函数 需要 函数参数 ,且这个 函数 被频繁调用则考虑使用 inline

4.2 noinline :禁用内联

在 inline 场景下,默认不允许把一个函数当作对象使用

image.png

因为经过 inline 优化,这个 preAction 对象根本就不存在
所以需要使用 noinline

image.png

4.3 crossinline :内联加强

下面的代码中, return会结束哪个函数的执行?

image.png

结合 inline 机制模拟编译后的伪代码可看出她结束的是外部的 main 方法

image.png

这是完全符合逻辑与期待的。 inline 函数的函数参数的Lambda表达式,允许使用 return

如果这个函数参数又被其他内部调用包裹了呢?

image.png

这时候就需要使用 crossinline

image.png

但是 return 就不可再使用,也就避免了 retrun 要退出谁的问题。

简单使用场景建议:

  • 函数的接收参数中有 函数类型参数 才需要考虑使用 inline
  • 函数类型参数 需要作为 对象 使用时使用 noinline
  • 函数类型参数 需要被间接调用时使用 crossinline

警告:跨Module使用 inline ,有以下问题

在一个Library声明一个类,定义一个protecte 的 inline 函数,并在内部Lambda中访问一个protecte 修饰的成员。在另一个App Module中继承这个类,并访问这个 inline 函数。则会报错:无权限访问

image.png

这似乎是一个kotlin的bug,至少编译器应该进行错误提示

5 操作符重载、中缀表达式

fun main() {
    val point = Point(10, 20)
    println("一元 负号 ${-point}")  // 输出“Point(x=-10, y=-20)”

    val p1 = Point(1,1)
    val p2 = Point(1,1)

    println("二元 + ${p1 + p2}")
    println("二元 - ${p1 - p2}")

    println("中缀 大于 ${p1 moreThan  p2}")

}
///操作符重载:覆盖基础操作符函数(以运算符号的方式调用函数)
///中缀表达式:自定义操作符(不能是运算符号 只能是字母)

/*
  kotlin 操作符 + - 等都是函数映射
  val a = 1,val b = 1 , a + b 实际上是访问的下面的函数
  public operator fun plus(other: Int): Int
  借助操作符重载我们可以对任意对象拓展出 + - * / 等操作符
 */

//操作符重载-------operator-----------------------
data class Point(val x: Int, val y: Int)

//一元 [-] 号 -obj
operator fun Point.unaryMinus() = Point(-x, -y)


//二元 [+] 号  obj1 + obj2
operator fun Point.plus(p: Point): Point {
    return Point(x+p.x,y+p.y)
}
//二元 [-] 号  obj1 - obj2
operator fun Point.minus(p: Point): Point {
    return Point(x-p.x,y-p.y)
}

/*
  kotlin 中允许使用 infix 关键字将函数声明为中缀表达式
  在调用上得到简化使用:a.moreThan(b) -> a moreThan b
 */

//中缀表达式----------infix---------------------
infix fun Point.moreThan(p:Point):Boolean{
    return x*y > p.x * p.y
}
复制代码

6.拓展属性,拓展函数,匿名拓展

kotlin拓展具备很广的应用场景,所以拓展的代码很容易膨胀,不易于管理,容易发生滥用导致耦合度提高,破外代码边界。

权限优先级建议:

  • 类私有
  • 文件私有
  • 包私有
  • 模块私有
  • 全局通用

6.1 拓展属性实战举例

image.png

6.2 拓展函数

image.png

6.3 匿名拓展

image.png

inline+匿名拓展,比如 let、apply 等内置拓展函数

参考:

  • kotlin官方文档
  • 扔物线Hencoder课程
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享