前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【读书笔记】《Kotlin in Action》学习笔记(下)

【读书笔记】《Kotlin in Action》学习笔记(下)

作者头像
IT自学不成才
发布2019-01-07 11:26:23
6460
发布2019-01-07 11:26:23
举报

【读书笔记】《Kotlin in Action》学习笔记(下)

2017-10-03 by Liuqingwen | Tags: Kotlin | Hits

一、前言

读书笔记的上部分请参考:【读书笔记】《Kotlin in Action》学习笔记(上)

另外,关于我在 mobilehub 微信留言中免费获赠中文版《 Kotlin 实战》书籍的留言我也贴上,当时我回答的时候一方面想着能意外收获一本书,另一方面还是非常想推荐这边书给读者朋友们!

二、笔记

1、 操作符重载要注意的

  • a += ba = a.plus(b) 或者 a.plusAssign(b) 两者都完全等同( + - * / % 一样)
代码语言:javascript
复制
val list = arrayListOf(1, 2)
list += 3                     //list = [1, 2, 3]
var newList = list + 4        //newList = [1, 2, 3, 4]
newList = list + listOf(5, 6) //newList = [1, 2, 3, 5, 6]
  • 如果 plusplusAssign 两个都有定义,参数也一样,那么会出现编译模糊问题( + - * / % 一样)
代码语言:javascript
复制
data class Point(var x:Int = 0, var y:Int = 0) {
    operator fun plus(otherPoint: Point):Point {
        return Point(otherPoint.x + this.x, otherPoint.y + this.y)
    }
}

fun main(vararg parameters:String) {
    var p_var = Point()
    val p_val = p_var + Point(1, 1)
    p_val += p_var //Error: val cannot be reassigned.
    p_var += p_val //OK!
}

上面的代码很显然是没问题的,注意 val 变量不能赋值。但是,如果添加下面的代码( 通过扩展给 Point 类新增 plusAssign 方法)就是画蛇添足,会出现问题:

代码语言:javascript
复制
operator fun Point.plusAssign(otherPoint:Point) {
    this.x += otherPoint.x
    this.y += otherPoint.y
}

fun main(vararg parameters:String) {
    var p_var = Point()
    val p_val = p_var + Point(1, 1)
    p_val += p_var 
    p_var += p_val //Error: Assignment operators ambiguity
}
  • 把上面的 plusAssign 方法签名(参数类型)改一下可以使用,但意义已经改变
代码语言:javascript
复制
operator fun Point.plusAssign(otherInt:Int) {
    this.x += otherInt
    this.y += otherInt
}

fun main(vararg parameters:String) {
    var p_var = Point()
    p_val += 99
}

2、 型变和协变( in 和 out )参数在构造函数中不受约束

这又是一个特例!我们知道,使用 in 的参数是不能作为输出返回的,而使用 out 则作为输出而不能作为参数传入,下面两个接口就是这样,弄反了就出问题:

代码语言:javascript
复制
interface IOutParameter<out T> {
    fun takeOut():T
}
interface IInParameter<T> : IOutParameter<T> {
    fun takeIn(`in`: T)
}

再看类的构造函数,这是不受形参限制的,注意参数的位置:

代码语言:javascript
复制
// Note that constructor parameters are in neither the [in] nor [out] position.
// Even if a type parameter is declared as out, you can still use it in a constructor parameter declaration
open class Animal
class Herd<out T: Animal>(vararg animals: T)

3、 使用形参的一个正确姿势

这是一个非常简单的问题,对于大部分人来说,由于缺乏经验,我把这一条也作为书签记录下来,提醒自己可以如何优化(下面是官方例子)。首先看原始版本,拷贝一个列表到另一个:

代码语言:javascript
复制
fun <T> copyDataVersion1(source: MutableList<T>, destination: MutableList<T>) {
    for (item in source) {
        destination.add(item)
    }
}

上面的代码其实不合理(后面有说明),难道一定要同类型才能复制吗? T 的子类不能被复制过去吗?那么根据这个问题有了下面的改进:

代码语言:javascript
复制
fun <T: R, R> copyDataVersion2(source: MutableList<T>, destination: MutableList<R>) {
    for (item in source) {
        destination.add(item)
    }
}

上面的代码搞定了子类的数据复制,到此结束!?当然没有, Kotlin 提供了一个更加优雅的解决方案,不信你看看下面的代码:

代码语言:javascript
复制
fun <T> copyDataVersion3(source: MutableList<out T>, destination: MutableList<T>) {
    for (item in source) {
        destination.add(item)
    }
}

什么叫做优化?什么叫做改进?学习了!下面是测试代码:

代码语言:javascript
复制
fun main(vararg parameters:String) {
    val source = arrayListOf(1, 2, 3)
    val destination = arrayListOf<Any>()
    //copyDataVersion1(source, destination) //Error! Cannot compile!
    copyDataVersion2(source, destination) //Fine.
    copyDataVersion3(source, destination) //Nice!
}

4、 Kotlin 中 DSL 使用带有 object 参数的中缀函数

我只想说,“厉害了,我的 Kotlin 哥”! Kotlin 中 DSL 真的很好用,像大名鼎鼎的 anko 库,使用 DSL 实现 Android Layout 非常给力啊,还有 SQL 数据库操作,另外用过一段时间的 TornadoFX ,用 DSL 写 GUI 程序也是给力极了!

看下面一句话,还是来自教材:

代码语言:javascript
复制
"kotlin" should start with "kot"

Sorry ,说错了,不是一句话,是一段代码!对,这段代码没啥稀奇的了,不就是中缀函数拼凑起来吗?

代码语言:javascript
复制
"kotlin".should(start).with("kot")

没错,但是他的精髓你发现了没?精髓在于 start 的妙用!它是一个 object 单例,那么既然是单例为啥不直接使用,还要去作为 should 函数的参数呢?这不是毫无意义吗? No !这是 DSL 哦,它并不是作为数据参数传递给函数,而是作为语法的一部分!!!因此你可以有很多 object ,作为不同的语法使用,这就是精髓之处啊!

我相信,看了下面的代码你就能一目了然、豁然开朗了!

代码语言:javascript
复制
object start
infix fun String.should(x: start): StartWrapper = StartWrapper(this)
class StartWrapper(val value: String) {
    infix fun with(prefix: String) = if (!value.startsWith(prefix)) throw AssertionError("String does not start with $prefix: $value") else println("OK")
}

激动的我赶紧写下了几行流利的英语:

代码语言:javascript
复制
"kotlin" should start with "kot"
"kotlin" should end with "in"
"kotlin" should have substring "otl"

5、 Bonus: 使用 inline 属性

对,你没看错,这是额外加的一个新姿势,并不是从《 Kotlin in Action 》书中学到的,看到了我就马上记下来了,写到一起作为学习笔记吧。

参考以下代码,扩展一个属性非常简单:

代码语言:javascript
复制
inline var View.isVisible
    get() = visiblity = Visible

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_activity_main)
        val button = this.findViewById<Button>(R.id.button)
        if(button.isVisible) {
            toast("I am visible!")
        }
    }
}

毫无疑问代码是没有问题的,那么我们看下反编译 Kotlin 后的 Java 代码(无关省略):

代码语言:javascript
复制
if(GlobalKt.isVisible((View)button)) {
    ToastsKt.toast(this, (CharSequence)"I am visible!");
}

很正常啊, Kotlin 的风格,使用静态方法完成扩展呀。但是,我就是没想到为啥不用 inline 呢?省去静态方法,不是更快更方便吗?

代码语言:javascript
复制
val View.isVisible
    inline get() = this.visibility == View.VISIBLE

反编译后:

代码语言:javascript
复制
View $receiver$iv = (View)button;
if($receiver$iv.getVisibility() == 0) {
    ToastsKt.toast(this, (CharSequence)"I am visible!");
}

是不是更加得体了呢?反正我是这么认为的,省去了没必要的静态类方法。另外, inline 也可以写得更加优雅,也有需要注意的地方哦:

代码语言:javascript
复制
inline val View.isVisible
    get() = this.visibility == View.VISIBLE

inline var View.someVarProperty:String
    get() = "OK"
    set(value) {
        println("Value was set!")
    }

//Error! Won't compile!
var upperCaseString:String = ""
    inline get() = field.toUpperCase()
    inline set(value) {
        println("Field set!")
    }

更多可以参考原文: Inlining Kotlin Properties

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【读书笔记】《Kotlin in Action》学习笔记(下)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档