Kotlin 超车指南

本文阅读大约半小时,写了很久,写到吐血....

前段时间在我的知识星球里统计了一下使用kotlin开发的人,感觉还不错,有十多个人在工作中已经正式使用了,我的知识星球《Hi Android》欢迎你的加入,我也应星友的需求写了一部分的kotlin基础知识,我也还在学习的过程中,为了帮助更多的星友认识到Kotlin for Android,所以花了点时间整理了这篇《Kotlin超车指南》,如果对你有所帮助,记得点个赞哦。

Kotlin 超车指南_第1张图片 图片

Kotlin 是一个用于现代多平台应用的静态编程语言,由JetBrains开发。

Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。

Kotlin已正式成为Android官方支持开发语言。

推荐我的慕课网Android实战课程,助你暴力提升Android技术。
Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固
https://coding.imooc.com/class/390.html

点击阅读原文直达课程详情

一.Kotlin下载

Kotlin支持命令行,Eclipse,Intellij IDEA,Android Studio 3.0+ 四种方式编程,那么我们学习Kotlin基础,肯定是优先前三者, 只有在开始Android开发的时候才会选择Android Studio

1.命令行

命令行安装的话需要去Github上下载工具包

https://github.com/JetBrains/kotlin/releases/tag/v1.3.61

选择 kotlin-compiler-xxx.zip 即可。

2.Eclipse安装

安装Eclipse之前需要安装JDK,这个没意见吧,然后去Eclipse官网下载:

https://www.eclipse.org/downloads/

Kotlin 超车指南_第2张图片 图片

当然,就像ADT一样,如果想要Eclipse支持Kotlin开发,还需要下载Kotlin的插件

https://marketplace.eclipse.org/content/kotlin-plugin-eclipse

不过离线安装一般都是老版本的Eclipse了,在新版本中可以使用Eclipse商店进行在线安装,点击Eclispe菜单 - help - Eclipse Markerplace 搜索kotlin 就可以了。

Kotlin 超车指南_第3张图片 图片

3.Intellij IDEA

Kotlin 超车指南_第4张图片 图片

IDEA安装起来还是很方便的,点击链接:

https://www.jetbrains.com/idea/download/

下载自由版本即可。

4.Android Studio

Kotlin 超车指南_第5张图片 图片

Android Studio 也是如此,安装即可。

https://developer.android.google.cn/studio

二.Kotlin历史

1.Kotlin名称由来

Kotlin 来源于一个岛屿的名字,全称是 Kotlin Island,是英语「科特林岛」之意。这个小岛属于俄罗斯。

2.Kotlin版本迭代

2010 年 :JetBrains 着手开发 Kotlin。

2011 年 7 月 :JetBrains 公开宣布 Kotlin。

2012 年 2 月 :JetBrains 在 Apache 2 许可证下开源了 Kotlin 的源码

2016 年 2 月 :JetBrains 发布了 Kotlin 1.0 版,算是比较稳定的正式版。

2017 年 :1.1.2(2017 年 6 月)。相比 Kotlin 1.0 版本时,和 Kotlin 有关的代码数目从 210 万行飞升到 1000 多万行。

此后还在不断的更新中…

3.Kotlin语言特点

简洁

简洁是Kotlin最主要的特点。Kotlin中数据类,类型推导,Lambda表达式和函数式编程都可以大大减少代码行数,使得代码更加简洁。

安全

kotlin和Java一样都是静态类型语言

Kotlin支持非空和可空类型,默认情况下Kotlin数据类型声明的变量都是不能接受空值(null)的。

类型推导

Kotlin编译器可以根据变量所在的上下文环境推导出它的数据类型

支持函数式编程

作为现代计算机语言Kotlin支持函数式编程,函数式编程优点:代码变得简洁,增加线程安全和便于测试。

支持面向对象

Kotlin支持函数式编程,但也不排除面向对象。

Java具有良好的互操作性

Kotlin和Java具有100%互操作性,Kotlin不需要任何转换成包装就可以调用Java对象。反之亦然,Kotlin完全可以使用现有的Java框架或库

免费开源

Kotlin源代码是开源免费的,它采用Apache2 许可证。

三.Kotlin基础

1.输出

1fun main(arg:Array){
2    println("Hello Kotlin!")
3}

main函数是我们的主程序入口,在控制台输出Hello Kotlin使用了println方法。

2.输入

有输出就有输入,不过输入是从外部传递的

1fun main(arg:Array){
2    println("请输入...")
3    var a = readLine()
4    println("你输入的内容是 $a")
5}

3.变量与常量

1fun main(arg:Array){
2    var name = "张三"
3    name = "李四"
4    println(name)
5
6    val age = 18
7    println(age)
8}

我们用var代表变量,即可变的量,用val声明常量,即不可变的量

4.数据类型

数据类型和Java是类似的

Byte Short String Int Long Float Double

5.类型推导

类型推导也是kotlin的一个特色,我们来看代码

1fun main(arg:Array){
2    var name:String
3    var age = 19
4}

如果没有值就无法类型推导,则需要声明数据类型,如果有值,则可智能推导类型

6.函数

我们先来看下一段示例代码

1//输出
2fun print(text:String){
3    println(text)
4}
5
6//加法
7fun add(a:Int,b:Int):Int{
8    return a+b
9}

首先是我们的输出函数,实际上所有的函数都有返回值的,但是有些不需要返回值,所有返回了一个Unit类型,也就是我们理解的无返回值,而加法两数相加之后需要把结果返回回去,则有了Int的返回值类型。

a.参数默认值

有时候碰到一些封装的场景,有对函数值统一要求的时候,可以这样处理

1fun main(arg:Array){
2    println(add(1,2))
3    println(add(b = 2))
4}
5
6//参数默认值
7fun add(a:Int = 5,b:Int):Int{
8    return a+b
9}

我给a默认赋值了一个5,如果使用默认值,则需要指定b = 2,如果不用此默认值,则直接传值即可,比重载好用。

7.字符串模板

在Java中要拼接字符串的话,需要很多的+号,但是有了字符串模板,一切都会变得简洁起来了

1fun main(arg:Array){
2    val name = "张三"
3    val address = "超市"
4    val apple = 10
5    val banana = 5
6    //张三 去 超市 买了苹果和香蕉一共花了 15 元
7    print("$name 去 $address 买了苹果和香蕉一共花了 ${apple + banana} 元")
8}

如果用java写,你可以想象要怎么拼接

8.条件判断

条件判断我们有if语句

1fun main(arg:Array){
2    val i = 10
3    if(i < 5){
4        println("i比5小")
5    }else{
6        println("i比5大")
7    }
8}

9.循环

循环使用in即可,在后面区间中使用

10.空类型

在Java中可能会出现空指针异常,但是在kotlin中是可以避免这种问题的,我们来看下空类型判断

 1fun main(arg:Array){
 2    //noNull(null) 报错
 3    println(yesNull(null))
 4}
 5
 6//不能为null
 7fun noNull(text:String):String{
 8    return "Hello $text"
 9}
10
11//可以为null
12fun yesNull(i:Int?):String{
13    if(i === null){
14        return "i == null"
15    }
16    return "Hello $i"
17}

如果我传值指定了是String,那么null作为空类型是无法传入的,如果想要传入null,则需要加上问号?

11.选择表达式

在java中可以使用switch,在kotlin中则是when了

 1fun printScore(sex:Boolean,i:Int){
 2    when(i){
 3        100 -> print("最高成绩")
 4        80 -> {
 5            if(sex){
 6                print("男生这个分数不行")
 7            }else{
 8                print("女这个分数还行")
 9            }
10        }
11        else -> print("其他人都重考")
12    }
13}

并且不管我们的if还是when都是有返回值的,默认为表达式的最后一行

 1fun printScore(sex:Boolean,i:Int){
 2    val result = when(i){
 3        100 -> 100
 4        80 -> {
 5            if(sex){
 6                80
 7            }else{
 8                90
 9            }
10        }
11        else -> 60
12    }
13    print("result $result");
14}

12.区间

区间很好理解,就是某个数到某个数之间,这里来计算一下从0到100的数字之和

1fun main(arg:Array){
2
3    var a = 0..100
4    var result = 0
5    for(num in a){
6        result = result + num
7    }
8    println("从0到100的数字总和: $result")
9}

区间还有开区间(a..b)和闭区间[a..b]的概念
闭区间包含了端点的两个值

开区间不包含端点的两个值

13.List

 1fun main(arg:Array){
 2    var a = listOf("吃早餐",1,"睡午觉",2)
 3    for(list in a){
 4        //取值
 5        println(list)
 6    }
 7    for((k,v) in a.withIndex()){
 8        //取下标与值
 9        println("k $k , v $v")
10    }
11}

我们可以通过listOf来创建list

14.map

键值对的存储,我们先简单理解一下

1fun main(arg:Array){
2    var map = mapOf("Java" to 86, "Kotlin" to 92, "Go" to 78)
3    for((k,v) in map){
4        println("k $k , v $v")
5    }
6}

我们通过mapOf返回一个不可变的map

15.函数式编程

函数式编程是不同于过程式编程的另一种编程范式。函数式编程的思想在许多方面和过程式是冲突的,比如,过程式编程倾向于描述“怎么做”,而函数式编程则更倾向于描述“做什么”,过程式倾向于使用变量,而函数式则倾向于使用常量。尽管如此,函数式和过程式依旧是可以共存的,我们来看一个简单的例子:

1fun add(a:Int,b:Int):Int{
2    return a+b
3}
4
5fun add1(a:Int,b:Int) = a+b
6val add2 = {a:Int,b:Int -> a+b}

上一个是标准的函数写法, 而中间,则将表达式作为返回值,而add2则直接声明一个val接收参数计算,这完全就打破了我们之前的编程写法。

16.数据类型转换

类型转换用的最多的就是字符串转Int了,我们看代码:

1fun main(arg:Array){
2    var a = "12"
3    println(a.toInt())
4}

其他转换也类型toLong,toString等。

17.递归

递归有两个概念,递归和尾递归,相当于调用自身,我们在java中也学过,再来看下尾递归,尾递归是一个新的概念,他的重要性在于它可以不在调用栈上面添加一个新的堆栈帧,而是更新它,如同迭代一般。

比如我们累加一个超级大的数,如果使用递归会出现堆栈溢出的吗,但是尾递归不会

 1fun main(arg:Array){
 2    var result = 0
 3    println(add(100,result))
 4}
 5
 6tailrec fun add(i:Int,result:Int):Int{
 7    //println("result:$result")
 8    if(i == 0){
 9        return result
10    } 
11    return add(i - 1,result + i)
12}

18.单例模式

单例模式在java中有很多写法,但是在kotlin中就简单多了,只需要将class改为object即可,相当于在jvm内存中直接开辟了一块唯一内存地址。

但是使用object只是相当于Java的饿汉式,如:

 1//Java
 2public class JavaSingleton {
 3    private static JavaSingleton instance= new JavaSingleton();
 4    private JavaSingleton(){
 5
 6    }
 7    public static JavaSingleton getInstance(){
 8        return instance;
 9    }
10}
11//Kotlin
12object KotlinSingleton

如果我们需要实现双重校验的单例模式的话,需要利用伴生对象和lazy来实现:

 1//Java
 2public class JavaSingleton {
 3    private volatile static JavaSingleton instance;
 4    private JavaSingleton(){}
 5    public static JavaSingleton getInstance(){
 6        if(instance==null){
 7            synchronized (JavaSingleton.class){
 8                if(instance==null){
 9                    instance=new JavaSingleton();
10                }
11            }
12        }
13        return instance;
14    }
15}
16//kotlin
17class KotlinSingleton private constructor() {
18    companion object {
19        val instance: KotlinSingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { KotlinSingleton() }
20    }
21}

19.枚举类

枚举很简单,稍微看看就行

 1fun main(args: Array) {
 2    println("现在是 ${Season.WINTER.name} 位于枚举第 ${Season.WINTER.ordinal} 个元素 ")
 3}
 4
 5//季节
 6enum class Season {
 7    SPRING,
 8    SUMMER,
 9    AUTUMN,
10    WINTER
11}

20.印章类

印章类又称为密封类,用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而它的一个子类可以有可包含状态的多个实例

 1fun main(args: Array) {
 2    var a = Son.chinaSon()
 3    var b = Son.foreignSon()
 4    a.Hello()
 5    b.Hello()
 6}
 7sealed class Son{
 8    fun Hello(){
 9        println("我是孩子")
10    }
11    class chinaSon:Son()
12    class foreignSon:Son()
13}

可以看到,假设中国人和外国人在一起生出的孩子,有可能是中国人的样貌,有可能是外国人的样貌取决于基因,那么我通过sealed来修饰。

21.数据类

数据类使用data修饰即可

22.延迟加载

延迟加载在kotlin中有两种,lateinit var 和 by lazy ,在kotlin中,默认属性都是空安全的,所以声明必须有初始化值,但是我们有时候是没有初始值的,这个时候就需要延迟加载了。

lateinit var 只能用于类中

1class People {
2
3    //报错
4    var names:String
5    lateinit var name:String
6}

而by lazy 作为惰性加载,其使用第一次的时候才会为你加载

1fun main(args: Array) {    
2    val p by lazy { People() }
3}

我们一般常用的还是lazy,不过需要注意的是
1.by lazy 只能作为val关键字的属性

2.当属性用到的时候才会回调括号内的内容

23.伴生对象

伴生对象的关键字是companion,为外部模拟静态成员,可以半理解为static

1class People {
2    companion object PeopleInstnce {
3        const val name: String = "张三"
4    }
5}
6
7fun main(args: Array) {
8    println("name ${People.name}")
9    println("name ${People.PeopleInstnce.name}")

}

要注意的是,每个类只能定义一个伴生对象,这个伴生对象相当于外部类的对象,可以直接通过外部类名访问伴生对象的成员,这也是取消了static的一个弥补手段。

24.内联函数

内联的作用主要还是为性能这块所考虑,我们每一次调用高阶函数的时候,都会创建一个新的对象,所以你可以发现高阶函数都是内联函数,内联函数的关键字是lnline

当一个函数被声明为inline时,它的函数体是内联的,也就是说,函数体会被直接替换到函数被调用地方。

四. 面向对象

1.对象

和java一样,我们来看下

1class People(var name: String, var sex: Boolean)
2
3fun main(args: Array) {
4    var p = People("张三", true);
5    println("人类${p.name}他的性别是${if (p.sex) "男" else "女"}")
6}

可以看到这里class定义一个类,然后声明他的参数,不需要去创建他的构造函数,实际上,这就是他的构造函数
那么构造函数,这里又有一些区分了,在kotlin中,有主构造和次构造的区别

而我们上述的写法真实情况是这样的,只不过我们一般省略关键字

1class People constructor(var name: String, var sex: Boolean)
2

我们如果没有在主构造中对变量进行var/val的声明,则可以在init语句中进行赋值

1class People(name: String, sex: Boolean) {
2    private var name: String? = ""
3    private var sex: Boolean? = false
4
5    init {
6        this.name = name
7        this.sex = sex
8    }
9}

当然,你也可以直接赋值,这也算写法上的演进

1class People(name: String, sex: Boolean) {
2    private var name: String? = name
3    private var sex: Boolean? = sex
4}

那么再来看下次构造,在Java中,构造函数是必须和类同名的,但是kotlin中则不是,我们可以这样

 1class People {
 2
 3    constructor(name: String)
 4    constructor(name: String, sex: Boolean)
 5}
 6
 7fun main(args: Array) {
 8    People("张三")
 9    People("李四", true)
10}

2封装

封装和java是一样的,只要不对外提供的代码进行private修饰即可,默认public

3.继承

继承的概念和java类似,但是还是有一些不一样的,我们来看下代码

 1open class People(var name: String, var sex: Boolean) {
 2    open fun Eat() {
 3        println("People Eat")
 4    }
 5    open fun Sleep() {
 6        println("People Sleep")
 7    }
 8
 9    open fun Character() {
10        println("People Character")
11    }
12}
13
14class Boy(name: String, sex: Boolean) : People(name, sex) {
15    override fun Character() {
16        println("Boy Character")
17    }
18}
19
20fun main(args: Array) {
21    val boy = Boy("张三",true);
22    boy.Eat()
23    boy.Sleep()
24    boy.Character()
25}

在这段代码中,我定义了一个People,他有吃,睡,性格,三个函数,现在一个男孩继承了他,并且重写了性格的函数,因为每个人的性格都是不一样的,这里和java需要注意的问题就是,在java中我们继承了就可以直接使用父类,但是在kotlin中,父类需要标记open这个关键字才能让人继承,并且子类重写方法需要加上overide

4.抽象

我们看继承贴出的那些代码片段可以发现,其实很多东西我们可以优化的,比如People作为父类没必要实现具体操作,可以抽象出来:

 1abstract class People(var name: String, var sex: Boolean) {
 2    abstract fun eat()
 3
 4    abstract fun sleep()
 5
 6    abstract fun character()
 7}
 8
 9class Boy(name: String, sex: Boolean) : People(name, sex) {
10    override fun eat() {
11        println("Boy eat")
12    }
13
14    override fun sleep() {
15        println("Boy sleep")
16    }
17
18    override fun character() {
19        println("Boy character")
20    }
21}
22
23fun main(args: Array) {
24    val boy = Boy("张三", true);
25    boy.eat()
26    boy.sleep()
27    boy.character()
28}

可以看到,我们通过abstract去优化了这个抽象类,而Boy类具体去实现

5.多态

多态我们可以看抽象类的延伸,即同种功能不同的实现

 1abstract class People(var name: String, var sex: Boolean) {
 2    abstract fun character()
 3}
 4
 5class Boy(name: String, sex: Boolean) : People(name, sex) {
 6    override fun character() {
 7        println("$name ${if (sex) "调皮" else "乖巧"}")
 8    }
 9}
10
11class Girl(name: String, sex: Boolean) : People(name, sex) {
12    override fun character() {
13        println("$name ${if (sex) "调皮" else "乖巧"}")
14    }
15
16}
17
18fun main(args: Array) {
19    val boy = Boy("张三", true);
20    boy.character()
21
22    val girl = Girl("李四", false);
23    girl.character()
24}

比如一个People的性格,男人和女人实现的内容是不一样的。

6.接口

接口使用的关键字是interface,比如我模拟一下点击事件

 1fun main(args: Array) {
 2
 3    //点击事件
 4    click(object :OnClickListener{
 5        override fun onClick(position: Int) {
 6            println("position:$position")
 7        }
 8    })
 9}
10
11fun click(listener: OnClickListener){
12    listener.onClick(2)
13}
14
15interface OnClickListener {
16    fun onClick(position: Int)
17}

7.委托和代理

委托和代理是相辅相成的,打个比方,政府招商的一块地,一千万委托给了开发商A,这个时候就是政府委托开发商,而开发商代理了政府,那么这个时候开发商A已一百万的价格委托给了开发商B,从中牟利了九百万,也就导致了现在的豆腐渣工程了,那么我们用代码来实现吧。

 1fun main(args: Array) {
 2
 3    val build = developersA("一千万")
 4    build.buildHome()
 5}
 6
 7class developersB(var price: String) : IBuildHome {
 8    override fun buildHome() {
 9        println("建造房子花费$price")
10    }
11}
12
13class developersA(var price: String) : IBuildHome by developersB("一百万")
14
15//建造方式的能力
16interface IBuildHome {
17    fun buildHome()
18}

可以看到我们是通过by关键字进行委托的,developersA 获得一千万后给developersB一百万依旧把房子建造出来了。

五.函数式编程

我们在讲kotlin基础的时候实际上是有讲函数式编程的,但是那些都比较粗糙了,这一章节我们讲细一点

1.闭包

我们需要先理解闭包的概念,因为后面讲高阶函数的时候

1.闭包指的是函数的运行环境

2.闭包可以持有函数的运行环境

3.函数内部可以定义函数

4.函数内部也可以定义类

5.在函数中返回一个函数,被返回的函数可以调用主函数的属性

这样可能理解有点抽象,事实上,作为kotlin的特性之一,闭包的用途还是很广泛的,全局变量,顾名思义,其作用域是当前文件甚至文件外的所有地方;而局部变量,我们只能再其有限的作用域里获取。

那么,如何在外部调用局部变量呢?答案就是——闭包,与此给闭包下个定义:闭包就是能够读取其他函数内部变量的函数

 1fun main(args: Array) {
 2    val add = add()
 3    add()
 4    add()
 5    add()
 6    //输出 1 2 3
 7}
 8
 9fun add(): () -> Unit {
10    var count = 0
11    return fun() {
12        println(++count)
13    }
14}

这段代码中可以看到输出的是 1 2 3 ,也就是在return的fun中引用了外部的count

2.forEach

forEach是高阶函数之一,我们先来看下他的用法

1fun main(args: Array) {
2    val lists = listOf(1,2,3,4,5,6)
3    lists.forEach {
4        println(it)
5    }
6}

直接就能打印lists的参数,而要想了解他的工作原理,我们最好来看下他的源码

1/**
2 * Performs the given [action] on each element.
3 */
[email protected]
5public inline fun  Iterable.forEach(action: (T) -> Unit): Unit {
6    for (element in this) action(element)
7}

可以看到他是Iterable的扩展函数,接收的是一个action参数,T就是list的类型,也就是

带有Int类型无返回值的函数

而内部则是通过for遍历传入下标得到具体值

还有一个forEachIndexed则是求下标和具体值的,这个自己理解一下

3.apply

apply函数扩展了所有的泛型对象,在闭包范围内可以任意调用该对象的任意方法,并在最后返回该对象.

我们来看一段示例代码

 1class People {
 2    val name: String = "小明"
 3    val age: Int = 18
 4}
 5
 6fun main(args: Array) {
 7    //方式一
 8    val p1 = People();
 9    println("p1 ${p1.name} ${p1.age} ")
10
11    //方式二
12    People().apply {
13        println("p1 $name $age")
14    }
15}

在闭包内我们可以随意获取和变化参数并且返回对象本身,我们来看下他的源码

[email protected]
2public inline fun  T.apply(block: T.() -> Unit): T {
3    contract {
4        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
5    }
6    block()
7    return this
8}

这段代码中T表示对象本身,并且内部又一个契约contract,用于上下文推断。

4.maxBy

求最大值

1fun main(args: Array) {
2    val lists = listOf(11,2,30,49,50,6)
3    val max = lists.maxBy { it }
4    println(max)
5}

我们可以传递一个类型进去,他则返回一个类型的最大值,来看下源码

 1public inline fun > Iterable.maxBy(selector: (T) -> R): T? {
 2    val iterator = iterator()
 3    if (!iterator.hasNext()) return null
 4    var maxElem = iterator.next()
 5    if (!iterator.hasNext()) return maxElem
 6    var maxValue = selector(maxElem)
 7    do {
 8        val e = iterator.next()
 9        val v = selector(e)
10        if (maxValue < v) {
11            maxElem = e
12            maxValue = v
13        }
14    } while (iterator.hasNext())
15    return maxElem
16}

这段源码实际上就是在遍历获得最大值,通过R返回回去,当然,还有minBy求最小值

5.fifter

过滤函数,要啥RxJava,要啥自行车,kotlin美滋滋

1fun main(args: Array) {
2    val lists = listOf(11, 2, 30, 49, 50, 6)
3    //求大于20且小于50的值
4    val filter = lists.filter { (it > 20) and (it < 50)}
5    println(filter)
6}

这里可以多条件判断,只要符合规范即可。
看源码得知最终调用的是filterTo

6.map

map作为数据转换,根据规则创建新的集合

1fun main(args: Array) {
2    val lists = listOf(11, 2, 30, 49, 50, 6)
3    val filter = lists.map {
4        it.toFloat()
5    }
6    println(filter)
7}

这里我对值进行了toFloat的操作,则全部类型变换为float.
看源码得知最终调用的是mapTo,但是可以知道是创建了一个ArrayList进行的返回

7.any

any更多的是作为一个判断函数使用

1fun main(args: Array) {
2    val lists = listOf(11, 2, 30, 49, 50, 6)
3    //集合中是否有30这个值
4    val filter = lists.any {
5        it == 30
6    }
7    println(filter)
8}

来看下源码

1public inline fun  Iterable.any(predicate: (T) -> Boolean): Boolean {
2    if (this is Collection && isEmpty()) return false
3    for (element in this) if (predicate(element)) return true
4    return false
5}

传递的参数是一个T的集合类型返回的是一个布尔的函数,可以看到他的实现也是通过for循环遍历的

8.count

count作为满足条件的统计函数,我们可以这样使用

1fun main(args: Array) {
2    val lists = listOf(56, 77, 30, 49, 100, 63)
3    //统计及格的同学人数
4    val filter = lists.count {
5        it >= 60
6    }
7    println(filter)
8}

从源码中也可以看出是通过for循环去遍历统计size的

9.find

查找函数,只返回满足条件的第一个

1fun main(args: Array) {
2    val lists = listOf(56, 77, 30, 49, 100, 63)
3    //查找第一个及格的同学
4    val filter = lists.find {
5        it >= 60
6    }
7    println(filter)
8}

看源码得知最终调用的是firstOrNull

10.groupBy

这是分组函数,用于一个特定条件的分组

1fun main(args: Array) {
2    val lists = listOf(56, 77, 30, 49, 100, 63)
3    //及格和不及格的分组
4    val groupBy = lists.groupBy {
5        it >= 60
6    }
7    println(groupBy)
8}

比如这个示例,我的条件是大于等60则一组,那么其余为一组,可得及格和不及格的分组信息

11.let

let的作用主要用于避免非空的判断和作用域替换

1name?.let{
2    //只有不为空才会走进来
3}

六.DSL

DSL领域特定语言,即好玩又好吃的语法糖。

1.扩展函数

Kotlin的扩展函数可以让你作为一个类成员进行调用的函数,但是是定义在这个类的外部。这样可以很方便的扩展一个已经存在的类,为它添加额外的方法。在Kotlin源码中,有大量的扩展函数来扩展java,这样使得Kotlin比java更方便使用,效率更高。

我们来举个例子:

1fun main(args: Array) {
2    val lists = listOf(56, 77, 30, 49, 100, 63)
3    println(lists.getIndex())
4}
5//扩展:获取第一个下标
6fun List.getIndex():Int{
7    return get(0)
8}

我现在想获取List的第一个值,但是如果List并没有这个方法,我则可以直接给予他扩展。

2.中缀表达式

像我们的and or in 都是中缀表达式,实际上就是省略了点的操作

 1fun main(args: Array) {
 2    Boy().love(Girl())
 3    Girl() love Boy()
 4}
 5
 6class Boy{
 7    fun love(girl: Girl){
 8        //普通写法
 9    }
10}
11
12class Girl{
13    infix fun love(boy: Boy){
14        //中缀表达式
15    }
16}

可以看到,是相当的有趣。

七.Kotlin For Android

关于Kotlin开发Android应用的时候的一些区别

1.创建项目

创建项目的时候,在Configure your peoject 这一项的时候选择kotlin作为开发语言即可

图片

大体的东西都不会变化,只是增加了对Kotlin的支持,包括在project/build.gradle中增加了kotlin的插件

Kotlin 超车指南_第6张图片 图片

以及在app/build.gradle中增加了kotlin的stdlib和core库,当然还包括了kotlin的扩展库

Kotlin 超车指南_第7张图片 图片

2.findViewById

kt是不需要自己手动findviewbyid的,我们可以用个扩展插件来完成,先来看一段代码

 1import kotlinx.android.synthetic.main.activity_main.*
 2
 3class MainActivity : AppCompatActivity() {
 4
 5    override fun onCreate(savedInstanceState: Bundle?) {
 6        super.onCreate(savedInstanceState)
 7        setContentView(R.layout.activity_main)
 8
 9        mButton.text = "Button"
10        mButton.setOnClickListener {
11            Log.i("TAG", "Button Click")
12        }
13    }
14}

在这段代码中,我们引入了一段包名

1import kotlinx.android.synthetic.main.activity_main.*

我们就可以直接省略初始化的操作,使用id就可以了
这个功能就是kotlin扩展库的功能了

3.跳转界面

1//Java
2startActivity(new Intent(this,FirstActivity.class))
3//Kotlin
4startActivity(Intent(this,FirstActivity::class.java))

跳转主要还是在class类上,我们可以看到Kotlin需要::class.java

4.匿名内部类

1mButton.setOnClickListener(object :View.OnClickListener{
2    override fun onClick(v: View?) {
3        //Anything
4    }
5})
6//可简化
7mButton.setOnClickListener { 
8
9}

5.Anko

这是一个优秀的kotlin辅助库,也可以称之为工具库:

https://github.com/Kotlin/anko

a.代码布局

可以使用它的一些语法糖来实现代码写布局,虽然我觉得这个使用率肯定是不高的

 1verticalLayout {
 2    gravity = Gravity.CENTER
 3    val name = editText()
 4    val password = editText()
 5    name.hint = "请输入账号"
 6    password.hint = "请输入密码"
 7    button("登录") {
 8        onClick {
 9            toast("登录成功")
10        }
11    }
12}

我们来运行看一下

Kotlin 超车指南_第8张图片 图片

b.Intent 优化

intent跳转也相对的简化了

1//需要设置FLAG
2startActivity(intentFor("id" to 5).singleTop())
3//需要传递参数
4startActivity("id" to 5)
5//仅适用跳转
6startActivity()

c.其他

还有log的优化,以及toast,dialog等,大家自己去发现吧。

八.实战案例:天气预报

这里我准备写一个Kotlin版本的天气预报来给各位演示下一些Koltin语法的基本使用,先来看下效果图

Kotlin 超车指南_第9张图片 图片

功能也很简单,共四个页面

  • 1.启动页,缩放动画

  • 2.城市选择页,自定义View实现城市选择,RecyclerView与城市选择View双向绑定和联动

  • 3.主页,天气详情,RecyclerView显示五天天气

  • 4.设置页,重新选择城市等

使用的接口是聚合数据

使用的网络请求库是retrofit2,好了,我们开始吧;

1.启动页

启动页就一个动画

Kotlin 超车指南_第10张图片 图片

这里使用的是ViewCompat自带的动画,追其根源的话最终还是属性动画实现,监听动画结束之后就可以判断是否选择过城市了,选择过就直接进入主页,没有的话就进入城市选择页,这里作为持久化保存,我是通过Kotlin的单例封装的一个SharedPreferences类

Kotlin 超车指南_第11张图片 图片

2.城市选择页

城市选择页比较麻烦,我们先来定义请求网络的接口

Kotlin 超车指南_第12张图片 图片

然后就是一层简单的封装了

Kotlin 超车指南_第13张图片 图片

这里首先是对类进行了object的单例化,然后对retrofit2进行了延迟初始化,当我们加载城市列表成功之后,我们就将数据设置给我们的侧边城市选择View,这里我贴一下核心的代码

Kotlin 超车指南_第14张图片 图片

首先就是绘制了,将34个城市名称按照View的高度进行平均分配和绘制,然后紧接着就是滑动的处理了

Kotlin 超车指南_第15张图片 图片

这样,我们就可以很轻松的实现列表的滑动了,但是这里又有一个问题了,那就是联动的问题

a.View -> RecyclerView

先看选择View控制列表的滑动

Kotlin 超车指南_第16张图片 图片

我这里的做法就是根据他的滑动坐标点,找到对应的城市名,然后根据城市名去过滤城市列表以此来找到index,这样就可以滚动列表了

b.RecyclerView -> View

这个是反向的操作,我们需要监听列表的滚动

Kotlin 超车指南_第17张图片 图片

得到第一个可见的下标后以此来推敲出城市名,然后去根据城市名得到index,让View去刷新即可

3.主页

主页的操作其实就是按部就班的将请求的数据显示出来

Kotlin 超车指南_第18张图片 图片

4.设置页

设置页也是一样的,这里多了个粘贴板的操作罢了

Kotlin 超车指南_第19张图片 图片

5.Gif预览

最后我们来预览下Gif

图片

九.结语

Kotlin之美还有很多,一文很难讲完,但是循序渐进,还是可以看到效果的,这篇文章的初衷还是希望带领大家走进这门语言,虽然Google强调Kotlin First ,但是就目前而言还只是应用App还没设计到Android 源码层的改动,所以还是百花齐放的阶段,留给大家的时间还是有的,希望大家再接再厉吧。

Github地址:

https://github.com/LiuGuiLinAndroid/Kotlin

推荐我的慕课网Android实战课程,助你暴力提升Android技术。
Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固
https://coding.imooc.com/class/390.html

点击阅读原文直达课程详情

如果有兴趣的话,可以加入我的Kotlin学习小组

Kotlin 超车指南_第20张图片 图片

我的公众号,期待你的关注

Kotlin 超车指南_第21张图片 图片

点击阅读原文查看更多内容

你可能感兴趣的:(Kotlin 超车指南)