Kotlin开发总结

纲要


  1. 前言
  2. Kotlin是什么?
  3. 为什么使用Kotlin?
  4. Kotlin常用特性有哪些(与Java比较)?
  5. 如何在开发中集成Kotlin?
  6. 使用Kotlin常见问题有哪些?

前言


从2017年秋季接触Kotlin以来,开始3个月写了一些学习Demo,最近半年才开始用Kotlin开发企业级的APP,遇到了一点坑,但整体感觉:简洁、高效,以至于现在丝毫不想写又臭又长的Java代码。
还在坚持写Java的同学不妨试试Kotlin,一定会让你流连忘返、乐不思Java ,:p。

Kotlin是什么?


简介

Kotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。
主要由JetBrains的圣彼得堡团队所开发,其名称来自于圣彼得堡附近的科特林岛。

静态动态类型编程语言:通俗讲
静态类型指的是编译器在compile time执行类型检查,动态类型指的是编译器(虚拟机)在runtime执行类型检查

JVM语言 Kotlin vs Java

Kotlin和Java源文件都需要先编译成符合JVM规范的字节码文件,才能够在JVM运行。因此,本质上讲

  1. Java能做的,Kotlin都能做,反之亦然。
  2. Java和Kotlin能够相互转换。通过字节码文件可以反编译为对方的源码文件。
Kotlin和Java编译流程对比

应用范围

适用构建Kotlin应用的平台(引自官网)

JVM-Java服务端开发(替代Java)
Android
Browser-JavaScript开发(替代JavaScript)
Native-C相关开发(替代C、Object-C和Swift)

历史

Kotlin历史版本

  • 2011年7月-JetBrains推出Kotlin项目
  • 2016年2月-发布v1.0版本
  • 2017年11月28日-v1.2
  • 2018年6月14日-v1.2.50

Google支持情况

为什么使用Kotlin?


通用理由

引自官网中文站

参考示例见官方
https://www.kotlincn.net/

使用Kotlin开发Android的理由

  • 兼容性:Kotlin 与 JDK 6 完全兼容,保障了 Kotlin 应用程序可以在较旧的 Android 设备上运行而无任何问题。Kotlin 工具在 Android Studio 中会完全支持,并且兼容 Android 构建系统。
  • 性能:由于非常相似的字节码结构,Kotlin 应用程序的运行速度与 Java 类似。 随着 Kotlin 对内联函数的支持,使用 lambda 表达式的代码通常比用 Java 写的代码运行得更快。
  • 互操作性:Kotlin 可与 Java 进行 100% 的互操作,允许在 Kotlin 应用程序中使用所有现有的 Android 库 。这包括注解处理,所以数据绑定和 Dagger 也是一样。
  • 占用:Kotlin 具有非常紧凑的运行时库,可以通过使用 ProGuard 进一步减少。 在实际应用程序中,Kotlin 运行时只增加几百个方法以及 .apk 文件不到 100K 大小。
  • 编译时长:Kotlin 支持高效的增量编译,所以对于清理构建会有额外的开销,增量构建通常与 Java 一样快或者更快
  • 学习曲线:对于 Java 开发人员,Kotlin 入门很容易。包含在 Kotlin 插件中的自动 Java 到 Kotlin 的转换器有助于迈出第一步。Kotlin 心印 通过一系列互动练习提供了语言主要功能的指南。

另外的理由

  1. 不用写烦人的findViewById
  2. Android Studio支持Java To Kotlin一键转换

Kotlin常用特性有哪些(与Java比较)?


具体语法可见Kotlin官网,或对应的中文版
以下主要说一下常用的语法或特性

基本语法

定义变量
局部变量-kotlin

    fun testDefination() {
        // 不可变
        val a: Int = 1  // 立即赋值
        val b = 2   // 自动推断出 `Int` 类型
        val c: Int  // 如果没有初始值类型不能省略
        c = 3       // 明确赋值

        // 可变
        var x = 5 // 自动推断出 `Int` 类型
        x += 1
    }

局部变量-java

    public void testDefination() {
        final int a = 1;// 可变
        int b = 1;// 不可变
    }

成员变量的懒加载和延迟加载-kotlin

    // 懒加载
    private val freshman by lazy {
        Person("feifei", 18, true)
    }
    // 延迟加载。定义成员变量时必须初始化,但不想初始化或不想引入空时,可以使用延迟加载
    private lateinit var old: Person

成员变量的懒加载和延迟加载-java?

字符串模板
kotlin

    @Test fun testStringTemplates() {
        val age = 12
        val name = "feifei"
        print("name -> $name, age -> $age")
    }

java

    public void testPrintString() {
        String name = "feifei";
        int age = 18;
        System.out.print("name -> " + name + ",age ->" + age);
    }

Elvis 操作符?

    // 判空。如果str=null,会打印:Length = null
    @Test fun testNull (str: String?) {
        println("Length = " + str?.length)
    }

    // 类型转换
    @Test fun testCast() {
        val l2 = listOf("A",1,3,6,8,'c')
        l2.forEach { println(it as? Int) }
    }

断言操作符!!

    private var p1: Person? = null
    @Test
    fun testAssert() {
        p1 = Person("feifei", 18, true)
        println(p1!!.name)
    }

条件表达式

    @Test
    fun testCondition() {
        // 空条件
        val files = File("Test").listFiles()
        println(files?.size ?: "empty")

        // if-else
        println("max -> ${maxOf(1,2)}")
        
        // when
        val items = listOf("apple", "banana", "kiwifruit", "grape", "pear", "ab...")
        when {
            "orange" in items -> println("juicy")
            "apple" in items -> println("apple is fine too")
        }
        
        // when
        describe(items[0])
    }

    fun maxOf(a: Int, b: Int): Int {
        return if (a > b) {
            a
        } else {
            b
        }
    }

    fun describe(obj: Any): String =
            when (obj) {
                1 -> "One"
                "Hello" -> "Greeting"
                is Long -> "Long"
                !is String -> "Not a string"
                else -> "Unknown"
            }

循环表达式和集合

    @Test
    fun testLoop() {
        val items = listOf("apple", "banana", "kiwifruit", "grape", "pear", "ab...")
        // for loop
        testForLoop(items)
        // collection
        testCollection(items)
    }

    private fun testForLoop(items: List<String>) {
        for (item in items) {
            println(item)
        }
        for (index in items.indices) {
            println("item at $index is ${items[index]}")
        }
        for (index in 0 until items.size) {
            println("item at $index is ${items[index]}")
        }
        for (index in items.size - 1 downTo 0 step 2) {
            println("item at $index is ${items[index]}")
        }
    }

    private fun testCollection(items: List<String>) {
        items.filter { it.startsWith("a") }
                .sortedBy { it }
                .map { it.toUpperCase() }
                .forEach { println(it) }
    }

类型检测

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` 在该条件分支内自动转换成 `String`
        return obj.length
    }

    // 在离开类型检测分支后,`obj` 仍然是 `Any` 类型
    return null
}

默认参数

    @Test
    fun testDefaultArgs() {
        println(createPerson("feifei1"))
        println(createPerson("feifei2", 12))
        println(createPerson("feifei3", 12, true))
    }

    private fun createPerson(name:String, age:Int = 0, isMale: Boolean = false):Person {
        return Person(name, age, isMale)
    }

数据模型

    // 会为 Customer 类提供以下功能:
    // 所有属性的 getters (对于 var 定义的还有 setters)
    // equals()/hashCode()/toString()/copy()等
    data class Customer(val name: String, val email: String)

单例

    object Resource {
        val name = "Name"
    }

常量

class KotlinSyntax {
    companion object {
        const val PI = 3.14
    }
}

// 或者
   object Resource {
        val name = "Name"
        const val PI = 3.14
    }

方法作为其他方法的参数
一般用于回调

// 场景,有一个轮播条,点击其中一个进入详情
// 定义一个adapter,传入值和对调函数
class BannerPagerAdapter (var srcs: List<String>,
                          private val callback: (position: Int)->Unit) : PagerAdapter() {
    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        val view = LayoutInflater.from(container.context).inflate(R.layout.view_home_headline, container, false)
        val tvHeard = view.findViewById<TextView>(R.id.iv_headline)
        // 设置点击监听
        tvHeard.setOnClickListener({callback(srcs[position])})
        container.addView(view)
        return view
    }
    // ...
}
    
    // 在Activity中设置
    private val bannerAdapter by lazy {
        BannerPagerAdapter(list_banner, callback = { title ->
            toBannerDetail(title)
        })
    }

    private fun toBannerDetail(title: String) {
        // to detail
    }

// or 简单的例子
    @Test
    fun testHighOrderFunc() {
        val items = listOf("a", "b", "c")
        wrapDoSomething { println(items) }
    }

    private fun wrapDoSomething(doSomething:()->Unit) {
        println("-->")
        doSomething()
        println("<--")
    }

范围外 run,let,with,apply

class Turtle {
        fun penDown(){}
        fun penUp(){}
        fun turn(degrees: Double){}
        fun forward(pixels: Double){}
    }

    // 对一个对象实例调用多个方法 (with)
    @Test
    fun testWith() {
        val myTurtle = Turtle()
        with(myTurtle) { // 画一个 100 像素的正方形
            penDown()
            for(i in 1..4) {
                forward(100.0)
                turn(90.0)
            }
            penUp()
        }
    }

Java调用Kotlin

object KotlinObject {

    const val PI = 3.14

    fun max(a: Int, b: Int): Int {
        return if (a > b) {
            a
        } else {
            b
        }
    }
}

class KotlinClass {
    companion object {
        const val PI = 3.14
        fun max(a: Int, b: Int): Int {
            return if (a > b) {
                a
            } else {
                b
            }
        }
    }
}
    public void useKotlin() {
        // use object
        System.out.print(KotlinObject.PI);
        System.out.print(KotlinObject.INSTANCE.max(1,2));
        
        // use class static const or fun
        System.out.print(KotlinClass.PI);
        System.out.print(KotlinClass.Companion.max(1,2));
    }

用于Android开发的工具

  • Kotlin Android 扩展是一个编译器扩展, 可以让你摆脱代码中的 findViewById() 调用,并将其替换为合成的编译器生成的属性。
  • Anko 是一个提供围绕 Android API 的 Kotlin 友好的包装器的库 ,以及一个可以用 Kotlin 代码替换布局 .xml 文件的 DSL。

如何在开发中使用Kotlin


非Android开发

请见官方文档,或对应中文版

Android开发

创建新项目
Android Studio 3.0+完全支持kotlin,创建项目的时候,默认勾选支持Kotlin,创建完项目后会自动在gradle文件中引入插件和扩展插件。

Android Studio 3.0+创建新项目

创建完工程后,工程gradle文件中


工程gradle文件

应用Module gradle文件中


应用Module gradle文件

Java 转 Kotlin

  1. 切到要转换的Java文件
  2. Android Studio菜单 Code -> ‘Convert Java File to Kotlin File’, 或 Mac环境下,双击Shite键,输入 covert java....出现‘Convert Java File to Kotlin File’选项。

Java转kotlin的常见问题
一般而言可以直接转换,无需修改,但有时需要手动修改,一般在如下几个地方出现:

  1. 逻辑操作符。kotlin中只有Int类型的值才能直接进行逻辑运算,其他类型如Byte要转为Int之后才能进行逻辑运算。且,kotlin中的逻辑运算是作为方法出现的。

    逻辑运算 Java vs Kotlin

  2. 单行赋值后进行比较
    这个在读流操作时经常出现

    private void readIS(InputStream is) throws Exception {
        int len = 0;
        byte[] bytes = new byte[1024];
        OutputStream os = new FileOutputStream("/file/text.txt");
        while ((len = is.read(bytes)) != -1) {
            os.write(bytes, 0, len);
        }
    }

直接转换成kotlin是

   @Throws(Exception::class)
    private fun readIS(`is`: InputStream) { // is是kotlin关键字,所有做了处理
        var len = 0
        val bytes = ByteArray(1024)
        val os = FileOutputStream("/file/text.txt")
        while ((len = `is`.read(bytes)) != -1) {  // 这行报语法错误
            os.write(bytes, 0, len)
        }
    }

// 稍作修改,消除语法错误
    @Throws(Exception::class)
    private fun readIS(`is`: InputStream) {
        var len = 0
        val bytes = ByteArray(1024)
        val os = FileOutputStream("/file/text.txt")
        len = `is`.read(bytes)
        while (len != -1) {
            os.write(bytes, 0, len)
            len = `is`.read(bytes)
        }
    }

使用Kotlin常见问题有哪些?


待续

参考


Kotlin官网
Kotlin中文网
Android开发者官方
Android Kotlin Guides
Kotlin_Tips
Kotlin简单编译流程
Kotlin编译过程分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容