Kotlin-Android的另一番风味

微信 订阅号助手 的Android App项目首次尝试使用Kotlin进行大规模的业务开发(483个Kt文件,3.8W行不包含空行的Kt代码),一开始接触Kotlin的时候难免会有点不适应,但经过几天的强制使用后,慢慢有些感觉,项目落地后回顾了一下,发现Kotlin确实是有它独特的风味

烹饪准备

  • 食材
    • Android,主要食材(指Framework、Api等),
    • Kotlin,食用安全、味鲜(扩展函数)、香(重载)、甜(富含糖份Lambda),第二主要食材,切好块状
    • Java,少量,Kotlin这种食材需要它来做引子
  • 锅,AndroidStudio、Eclipse这两个牌子的锅质量都不错
  • 调味料,Kotlin Android Extension、Android KTX、AndroidX、Anko等
  • 如果没有上述这些材料请移步到如下网址"购买"
    • https://developers.google.com/android
    • https://kotlinlang.org/docs/reference
    • https://www.oracle.com/java

烹饪过程

  • 开火,放少量食用油
  • 先把Android倒进去,伴两下
  • 倒少量Java,主要是"字节码"和"工具部分",再伴两下
  • 把切好块的Kotlin一块块慢慢平铺在Android上面,把Android盖住
  • 慢火煮3-5分钟,观察一下这个过程
    • Kotlin把Android的味道慢慢释放出来,比Android + Java更香
    • Kotlin与Java融为一体 (前提是少量Java,如果Java放得太多,香味会受影响,粘合不够好,容易松散(NPE))
  • 关火,焖一会

开锅,上菜

色香味倶全,敬请尽情享受这番独特的风味

特色风味一,食用安全

食用安全,Nullable or NotNul从源头抓起,Kotlin代码安全性更强

var output: String
output = null   // Compilation error

val name: String? = null    // Nullable type
println(name.length())      // Compilation error

食用安全从从源头上抓起,只要跟定义不符就编译不通过,这是Kotlin小而精的一个优点,一下子能把整碟"菜"的安全系数提高,此Code来自官方文档

特色风味二,鲜

扩展函数,味道鲜美,百吃不厌

项目工具类的另一种写法

fun String.toIntSafely(defaultValue: Int = 0): Int {
    return try {
        this.toInt()
    } catch (e: Exception) {
        defaultValue
    }
}

fun main(args: Array<String>) {
    println("1".toIntSafely())
}

String 转 Int,这种需求几乎很多项目都是需要,像上述Kotlin如果是在Java里面描述的话,估计会写成这样

public final class StringUtil{
    private StringUtil() {}
    public static int stringToInt(String string, int defaultValue) {
        //省略
    }
}

使用时

StringUtil.stringToInt("1", 0);

大家看到这里可能会觉得没什么,大家都是工具类,用的时候有些小差别而已

但正因为这些小差别,优点就体现出来了,确实是鲜美

  • 不需要记住工具类的名字和方法的名字,假如你是一个刚接手项目的新人,正准备做一个需求开发,突然需要这种String to Int的工具,但是不知道工具在哪,这就好比你去到一个陌生人的家里,想找个螺丝刀拧个松掉的螺丝一样,这“螺丝刀”在哪?除了问“主人”之外,要么就是“翻柜子”,这不就显得效率低么?使用Kotlin的扩展函数就能有效避免前面所说的问题,接手项目的新人只需要轻轻的“.”一下,滚两下鼠标,"toIntSafely"的方法就会看到。这就为什么你看Kotlin的Java扩展库很多都是通过扩展函数来封装
  • 方法的类归属更好理解,以上述的"toIntSafely"为例,String.toIntSafely,使得开发者更容易直观感受到这个函数是用于String,不像StringUtil.stringToInt没有归属可言,纯粹就是一个工具函数,不如Kotlin的写法容易理解
  • 对定义函数者的要求高了,正因体现了函数的类归属,也就使得开发者在定义函数的时候需要考虑归属给哪个类还是顶层函数这些问题,归属的范围少了,会导致不好用,范围广了又怕暴露导致滥用或者误用

特色风味三,香

重载(Overload),回味无穷

虽然这个概念在面向对象领域用得很多,但Kotlin这个重载的味道真是令我们吃上瘾

重载在工具类的场景用得非常多,一个项目下来没工具类也是不可能,例如我们在项目中会封装一些对话框(Dialog)工具类供开发的同学一句调用

  • 开发的同学需要在界面显示一个Dialog,只想改变Dialog的内容,那么Java里面就有showDialog(String message)的写法
  • 开发的同学需要在界面显示一个Dialog,即想改变Dialog的标题,又想改变Dialog的内容,那么Java里面就有showDialog(String title, String message)的写法
  • 开发的同学想改变Dialog里面Icon的....
  • 开发的同学想......

这些场景估计做Android开发的同学都会碰到,其实不限于Android,Java开发的同学也经常遇到,我们看看Kotlin是怎样把这些需求收拢

fun showDialog(title: String = "标题", message: String = "内容") {
    //TODO
}

这个写法一下子满足 2的2次方(4) 种重载方法

showDialog()

showDialog("新标题")

showDialog(message = "新内容")

showDialog("新标题", "新内容")

这种重载方式有效地减少我们项目中的重载方法数量,使得我们项目开发更简洁和更有效率 ,自然就回味无穷

特色风味四,甜而不腻

带了糖,甜而不腻

Kotlin里面Function与Lambda既可相互理解,又有其味道(写法)上的一些差异

味道 (结果) 一样,但味道消去的过程 (用法) 有差别

Function(函数)常用写法

fun f(x: Int): Any {
    return Any()
}

用法

val y = f(1)    

Function(函数)的一种Lambda写法

fun f() = { x: Int -> Any() } 等价于 fun f(): (Int) -> Any = { x: Int -> Any() }

用法

val y = f()(1) 或 val y = f().invoke(1)

Lambda写法

val f = { x: Int -> Any() } 等价于 val f: (Int) -> Any = { x: Int -> Any }

用法

val y = f(1) 或 val y = f.invoke(1)

细节点:Function时,有"="跟没有"="意义不一样,有"="的时候可以理解右边( { x: Int -> Any() } )是 左边函数返回类型((Int) -> Any) 的实现

函数不用置疑,项目里面必备

Lambda

Lambda,语法糖,这是怎样的一种成份?

Lambda是长这样的

val block: () -> Unit = {}
val sum: (Int, Int) -> Int = { p1, p2 -> p1 + p2 }

Lambda令我们的项目减少了很多接口类,尤其是回调接口,我们项目几乎没有。一般的业务场景里面回调接口都会用得不少,Lambda能有效减少这种Callback接口的定义,少写不少接口类,事半功倍。

另lambda里面不能写return,最后一行的值就是返回值

从数学函数角度抽象理解

函数: y = f(x)

假设x与y都是Int类型

可以理解为 Kotlin 函数:

fun f(x: Int): Int {
    return 1 // 这里的返回值就是对应y
}

也可以理解为 Lambda:

val f = { x: Int -> 1 } 等价于 val f: (Int) -> Int = { x: Int -> 1 }

使用时f(1),但是如果像上述那种f(x)的kotlin函数与f(x)的lambda同时同名同方法签名存在,使用上要f(1)与f.invoke(1)来区分是函数调用还是lambda调用

假设x与y都是Lambda类型

x是Lambda类型 (Int) -> Int ,y是Lambda类型 (Int) -> Int,可以换算成

fun f(x: (Int) -> Int): (Int) -> Int {
    return { it -> x(it) }
}

或这样

fun f(x: (Int) -> Int): (Int) -> Int = { it -> x(it) }

使用时

f { it -> it + 10 }(1) or f { it -> it + 10 }.invoke(1)

或 Lambda

val f: ((Int) -> Int) -> ((Int) -> Int) = { x -> { it -> x(it) } } // val时要inline

使用时

f.invoke { it -> it + 10 }.invoke(1)

通过上述的 替换 能更好地理解和使用Lambda

如何更好地了解Kotlin这种食材的味道

  • Kotlin用于Java领域,中间产物毫无疑问还是字节码,因此本质还是Java的基础知识,反编译Kotlin生成的字节码是学习Kotlin一种较好的方式,可利用AndroidStudio的Tools来反编译kt,能帮助快速理解Kotlin

谢谢品尝这份美味

希望Kotlin这款食材能带给各位读者不少Android上的特色的风味

参考食谱

https://kotlinlang.org

原文发布于微信公众号 - WeMobileDev(WeMobileDev)

原文发表时间:2018-11-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JadePeng的技术博客

知识图谱学习笔记(1)

RDF(Resource Description Framework),即资源描述框架,其本质是一个数据模型(Data Model)。它提供了一个统一的标准,用...

1890
来自专栏二次元

什么是递归?

一上来你肯定觉得读这句话好绕,好吃力。 其实你用递归来读就很简单了: 递归要有一个终点(小鲤鱼) 当递归尚未达到终点的时候,函数会反复调用自己。 ...

1120
来自专栏程序人生

谈谈数据结构

沃斯大神说过,程序 = 算法 + 数据结构。 程序君认为,等式的右边,数据结构的权重要大于算法。数据结构定义好,基本上,你所用的算法也就确定了,算法的时间复杂度...

3737
来自专栏iOS

深入理解苹果系统(Unicode)字符串的排序方法

本文主要讲述下载苹果系统-方法所引发的对排序规则的深入研究。

4527
来自专栏蓝天

Cuckoo Hash和多级Hash的粗浅认识

通过对Cuckoo Hash、多级Hash和BloomFilter的粗浅了解,感觉它们三者存在类似之处,算是近亲(暂且把普通的Hash称作远亲)。

910
来自专栏Hongten

让你一看就明白什么是代理模式--java版本_源码下载

======================================================

1743
来自专栏菩提树下的杨过

Flash/Flex学习笔记(47):反向运动学(上)

先回顾上篇所说的"正向运动学":以人行走的例子来说,基本上可以理解为大腿驱动小腿,小腿驱动脚,从而引发的一系列姿态调整和运动。再举一个例子,我们用着拿一根软鞭或...

1995
来自专栏小詹同学

Leetcode打卡 | No.18 四数之和

欢迎和小詹一起定期刷leetcode,每周一和周五更新一题,每一题都吃透,欢迎一题多解,寻找最优解!这个记录帖哪怕只有一个读者,小詹也会坚持刷下去的!

1082
来自专栏张善友的专栏

Clay: 创建和使用深层次对象图

Clay 是 CodePlex 上的一个开源项目,帮助我们创建轻松创建对象,就 JavaScript 或其它动态语言一样简单。Clay 项目的网址是 http:...

2066
来自专栏小樱的经验随笔

51Nod 1016 水仙花数 V2(组合数学,枚举打表法)

1016 水仙花数 V2 基准时间限制:1 秒 空间限制:131072 KB 分值: 160         难度:6级算法题 水仙花数是指一个 n 位数 ...

2887

扫码关注云+社区

领取腾讯云代金券