kotlin项目基础笔记

入职公司第三周时,项目组让我用kotlin去完成一个内部使用的app。下面记录一下kotlin相关笔记。因为app比较简单,可能很多kotlin特性没有用出来。学习kotlin过程中查找的资料放在了最后。
简书不支持生成目录,支持目录结构的文档地址

第一个kotlin的android项目实践

开发环境

AS version: android stdio 3.0 bate 5
android stdio 3.0以上是支持直接创建kotlin项目的,如果是android stdio 3.0以下,需要额外的添加一些依赖。

kotlin几个直观的特性

如果看不懂下面的内容没关系,后面会详细的讲,我也是菜鸟,看不懂就比较慌!

  1. java中必须使用;结尾。 而kotlin中一行代码结束是不需要使用;结尾的。
  1. 一个文件下,不一定需要一个类名,只要新建一个kotlin类型的文件(文件扩展名 为.kt的文件),就可以直接写代码。
    eg: 定义一个名为BaseExtension.kt的文件,功能是对类扩展了一些方法。
package manage.ad.yiba.com.admanage.common.extension

import android.content.Context
import android.support.v7.app.AlertDialog
import android.util.Log
import android.widget.Toast
import manage.ad.yiba.com.admanage.common.BaseActivity


/**
 * Created by yh on 8/24/17.
 *  该kt文件用来对类的扩展
 */

//Context 基础类的扩展
fun Context.toast(content: CharSequence, time: Int = Toast.LENGTH_LONG){
    Toast.makeText(this,content,time).show()
}

//扩展一个log方法
fun Any.log(tag: Any = "Tag",content: String?){

    Log.i(tag.toString(),content)
}
fun Any?.toString(): String {
    if (this == null) return "null"
// 在空检查之后,`this` 被自动转为非空类型,因此 toString() 可以被解
//    析到任何类的成员函数中
//    ChinaPerson("",2,"","","")
//    ChinaPerson("",2,"","","","")
    return toString()
}

fun BaseActivity.showDialogMessage(title: String = "", body: String, rightCode: ()->Unit = {System.exit(0)},leftCode: ()->Unit = {}) {
    var exitDialog: AlertDialog? = null
    val builder = AlertDialog.Builder(this)
    builder.setTitle(title).setMessage(body).setPositiveButton("OK") { dialog, which ->
        rightCode.invoke()
    }.setNeutralButton("Cancel"){dialog, which ->
        leftCode.invoke()
        try {
            exitDialog?.dismiss()
        }catch (e: Exception){
        }
    }

    exitDialog = builder.create()
    exitDialog.show()
}
  1. 一个扩展名为.kt文件中,可以定义多个类,这里定义的类跟javaBean是一个功能,这是在kotlin中的写法。
    eg: 名为Response.kt的文件中,包含多个类
package manage.ad.yiba.com.admanage.moudle.adDeploy

import manage.ad.yiba.com.admanage.retrofit.BaseResponse
import java.io.Serializable

/**
 * Created by yh on 9/19/17.
 */
data class AdDeployResponse(var data: MutableList<AdDeployData>): BaseResponse()

data class AdDeployData(var app: String = "", var appName: String = "", var token: String = "", var tokenStatus: Int = 0, var date: String = "", var type: Int = 0, var ids: String = "", var adPositions: String = "",var title: String = "",var viewType: Int = 1): Serializable


//详情页面

data class AppDetailResponse(var data: AppDetails):BaseResponse()

data class AppDetails(var app: String,var appName: String,var token: String,var tokenStatus: Int,var appDetails: MutableList<AppDetail>)

data class AppDetail(var adPlaceId: String,var app: String,var adStatus: Int,var serverId: Int,var sources: MutableList<Source>?):Serializable

data class Source(var id: String = "1",var packageName: String = "com.wifi.cool",var adPositionId: String = "1",var adCenterId: Int = 23,var level: Int =2,var centerName: String = "xxxx",var size: String = "RECT",var type: String = "DO"):Serializable

kotlin的基础

kotlin基本数据类型:
类型 字宽
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

在 Kotlin 中,所有的类型都是一个引用类型。在java中,分为基本数据类型和引用数据类型,这里需要注意的是,这些类型的首字母都是大写的

如何定义一个常量和变量
  1. 定义常量的关键字为val
val  a: Double = 27  //val + 变量名 +`:` + 空格+"类型" +" = " +"值"
val a = 27                //如果知道具体的类型,可以不带类型,kotlin会自动类型推断

注: a. 前面默认添加了public。

  1. 定义变量的关键字var
var name: String = "yhai"   //var + 变量名 +`:` + 空格+"类型" +" = " +"值"
var name = "yhai"              //如果知道具体的类型,可以不带类型,kotlin会自动类型推断

注意:
1.上面的空格是为了好的代码格式

  1. 对于变量,如果需要延迟初始化,需要在var 前面添加lateinit关键字。延迟初始化后面还会详细讲解。
lateinit var name: String

name = "yhai"
  1. 使用var修饰的变量,实际上也就拥有了get和set方法,而val修饰的没有set方法。
基本数据类型的转换
val value0: Int  = 23
val value1: Long = value0.toLong()
val value2: Double = value0.toDouble()
val value3: Float = value0.toFloat()

kotlin中的基本数据类型转换需要显示的调用对应的方法

数组的定义

java

Object[] objects = new String[2];
objects[0] = "0";
objects[1] = "1";

kotlin

    var array: Array<Any>
    var strings: Array<Any> = arrayOf("1", "2")
    array = strings

kotlin中数组使用Array<TYPE> 定义。上面的Any是kotlin中的最根上的类,可以理解为跟java中的Object一样。

字符串输出
val value = "hello,world"
println("str ==  $value")

val person = Person()
println("str ==  ${person.age}")

在""中,使用$value就能把value给打印出来,如果person 是一个复杂的对象,需要使用${表达式}

kotlin中创建一个对象不需要使用new关键字
java kotlin
Persion person = new Person() val person: Persoin = Person()
val person = Person() 自动类型推断

kotlin 中package 比较特殊,文件上的package 可以随便定义,例如:

package com.xxx.xxx.xxx
class  Person{
}

而这个文件真实的路径在com.yhai.kotlin目录下,在导入包的时候,需要注意下,否者看到这个文件在某个目录下,但是导入这个文件的包名并不是com.yhai.kotlin,而是com.xxx.xxx.xxx

控制流

if else
//传统用法
var max = a
if (a < b)
max = b
//带 else var max: Int if (a > b)
max = a
else
max = b
//作为表达式
val max = if (a > b) a else b

if 分支可以作为块,最后一个表达是是该块的值:

val max = if (a > b){
    print("Choose a")
    a
} else{
    print("Choose b")
b }

注:kotlin 的if判断跟java里面的使用方式差不多,唯一不同的是可以作为一个表达式有返回值。

when

用法一

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { //Note the block
        print("x is neither 1 nor 2")
    }
}
 

or
    when (item.itemId) {
            R.id.nav_setting->{

            }
            R.id.nav_share -> {

            }
            R.id.nav_send -> {

            }
        }

用法一

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { //Note the block
        print("x is neither 1 nor 2")
    }
}
 

用法二

when (x) {
    0,1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}
 

用法三

when (x) {
    parseInt(s) -> print("s encode x")
    else -> print("s does not encode x")
}

用法四:作为表达式

val hasPrefix = when (x) {
    is String -> x.startsWith("prefix")
    else -> false
}
while
while (x > 0) {
    x--
}
do {
    val y = retrieveData()
} while (y != null) // y 在这是可见的
for
for(item in collection){
}

对于各种循环,如何正确的终止呢?
参考学习资料

类和对象

类的定义

定义一个类更java中一样,使用class关键字。
第一种定义类的方式:没有参数,没有类体

class Person 

这样就定义了一个Person类,定义的这个Person类实际上是没有写错的。kotlin中如果这个类中没有属性,没有方法(后面会讲怎么跟这个类添加一个方法),类名后面的(){}就可以省略掉
第二种定义类的方式:有类体,没有参数

class Person{
}

第三种种定义类的方式:有参数,有类体

class Person(var name: String,var age: Int){
  //TODO 做些事情
}

在使用java的时候,我们在构造函数中传入参数,然后在构造函数体内做一些初始化的事情,而上面的构造函数的参数是直接跟在类名后面,在哪里做初始化的事情呢?实际上上面有个init{}代码块没写,完整的如下:

class Person(var name: String,var age: Int){
      init{
      //在这个位置做类似于java中构造函数体类做的事情。    
    }
}

这上面有个隐藏的知识点:

  1. 在构造函数中定义的参数使用var修饰,该参数将会在当前类全局都可以使用。不使用var修饰,那么该参数的使用范围只会在init{}中。这个我是通过将kotlin编译成.class,然后再翻译成java代码知道的。具体操作后面讲。

第四中定义类的方式:有参数,没有类体

class Person(val name: String,val age: Int)

如何定义多个构造函数:如果类有主构造函数,每个二级构造函数都要,或直接或间接通过另一个二级构造 函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:

class Person(val name: String,val age: Int ) {
    init{
          //这里会是最后调用的地方
      }
    constructor (name: String,age: Int,var other: String) : this(name,age) {
    }
}

注:在上面第一种定义类的地方,存在一个默认的构造函数,这个构造函数是不带参数的。

class Person(){
}
定义函数: 使用fun关键字,

格式: fun + 函数名+(参数列表)+"空格"+":" +"空格" 返回类型+{//函数体}

fun  dataIsModify(cache: List<String>,data: MutableList<String>) : Boolean{
    //...
    return false
}

注:1. 参数列表不能使用var 或val修饰,默认是有val修饰了。

  1. 空格是为了代码格式
函数参数列表的默认值:

我们定义函数的时候,调用函数的时候,必须要传入对应的参数,kotlin中,可以对参数列表设置默认的值,也就是说,有些参数可传可不传。

fun showDialogMessage(title: String = "", body: String = "this is a dialog") {
    //create a dialog 
}

调用上面这个函数:

showDialogMessage() //使用默认值
//或者只修改 body的值
showDialogMessage(body = "change dialog hint")//body 参数不使用默认值
继承

<span id="jump">跳转到的地方</span>
在kotlin中,类默认情况下是不能被继承的,需要在定义类的前面加上open关键字,该类才能被继承。同理,如果函数可以被继承,也需要使用open修饰,默认不能被继承。
继承的语法:使用: + 被继承的类名+(),()如果有参数的话,需要传递进去。

open class Base(var x: Int,var b: Int){
    open fun add() : Int{
        return x + b
    }
}
class Person(x: Int, b: Int) : Base(x,b){
}

注:在这里,Person类的构造中带有两个参数,没有使用var或者val修饰,原因是父类已经定义了x,b两个属性。在Person中定义就冲突了。如果一定要使用varval修饰,需要改变参数名;

open class Base(var x: String,var b: Int)

class Person(var xx: String,var  bb: Int) : Base(xx,bb){
}

//这里我遇到一个类似的场景,我在后面讲。
注: 如果要重写Base中的方法,那么该方法需要用override修饰,表示是重写的父类的方法。java中使用的是@override注解表示被重写的方法。

class Person(x: Int, b: Int) : Base(x,b){
    override fun add(): Int {
        return super.add()
    }
}
抽象类的定义:

使用abstract 关键字修饰,跟java中一样。同理,里面的抽象方法也需要abstract修饰。和java一样,也可以定义具体的方法。

abstract class Derived  {
    abstract fun f()  
    
    fun f2(){   
    }
}
sealed修饰的密封类

类的定义中,有一种密封类的概念
密封类相关资料

类的属性

之前有讲到定义一个变量和一个常量的方法,他们的完整的语法如下:

var <propertyName>: <PropertyType> [ = <property_initializer> ]
    <getter>
    <setter>

如果属性类型可以从初始化语 句或者类的成员函数中推断出来,那么他的类型也是忽略的。
语法中的初始化语句,getter 和 setter 都是可选的,他们是什么意思呢?如下:

var name : String
    get() = name// 可以对获取这个做一些修改。类似于java中的get方法体重做了一些事情。
    set(value) {
        //设置值做一些事情
    }
备用字段 或者叫做幕后字段 :field

这个概念的资料
我还没有怎么使用,所以放个链接,自己去看。

空安全

是时候讲下空安全是什么东西了。在kotln中,有一个很屌的功能,就是避免java中报null错误的处理方式。

var name: String = null  //语法不通过。必须对该属性赋值,而且这个值不能是null

//这样定义是语法检查不过的,因为你对name赋值为null,那么如果你一定要让这个name可以为null怎么做呢

var name: String? = null

//对,在类型String后面跟上一个?,大概意思是说,这个那么可以为空。
那么这个name使用的过程中,就有可能是个null,意味着要报null异常。为了防止报null,如下使用:

name?.toString()

在name上使用?. 调用它的方法。这样就能保证使用name避免出现null异常。
如果你定义了一个变量,这个变量为null,你还是坚决要调用他,怎么办呢?使用!!

   var name = null
    name!!.toString() // 这样就必然会报null异常

这里只要记住,?的意思是说可能为空,在调用可能为空的时候,使用?.避免异常和使用!!为空也要调用。

kotlin中接口的定义

kotlin中的接口中可以定义方法体,跟抽象类一样,这样接口和抽象类就一样了,我没有搞懂什么意思!接口的定义还是使用interface关键字

interface MyInterface {
   val property: Int // abstract
   val propertyWithImplementation: String
       get() = "foo"
   fun foo() {
       print(property)
} }

接口的实现:

class Child : MyInterface {
    override val property: Int = 29
}

接口实现和基础类的区别在于是有后面跟()

继承一个类和实现多个接口:
class Base(){

}
interface  MyInterface0 {
    override val property: Int = 29
}
interface MyInterface1 {
     fun  test(){
    }
}
class Child : Base(), MyInterface0, MyInterface1 {

}

解决重写冲突

多个接口中有相同的方法
在文档中直接copy的代码如下:

interface A {
    fun foo() { print("A") }
    fun bar() 
}
interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}
class C : A {
    override fun bar() { print("bar") }
}
class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
}

可见性修饰词

在 Kotlin 中有四种修饰:private,protected,internal,以及public,默认的为public。

private 只在该类(以及它的成员)中可见

protected 和 private 一样但在子类中也可见

internal 在本模块的所有可以访问到声明区域的均可以访问该类的所有成员 ( internal — any client inside this module who sees the declaring class sees its internal members;)

public 任何地方可见 (public — any client who sees the declaring class sees its public members.)

伴随对象

这个相当于java中的static 修饰的方法和属性.因为kotlin没有static关键字,实现静态方法的方式如下:在***companion object{} ***中de {}定义方法和属性就可以。

class Person7{
    companion object{
        var name = ""
        fun test(){  
        }
    }
}
Person7.test()//使用方式
扩展方法和属性和伴随对象的扩展

关于属性的扩展没怎么搞懂,下面不讲,项目中我暂时也没有用到。
在类定义的第一种定义方法中,定义了如下一个类

class Person

显然,定义了这么一个类是没有什么实际用途的。我们使用kotlin的扩展extension特性对其扩展一些方法。也就是说,我可以给所有的类添加方法。实现方式如下:

扩在函数格式:fun + "要对扩展的类的名字" + "."+方法名()+ "{} //方法体"
fun Person.eat(name: String){
    System.out.println("吃的是什么 = $name")
}

这样Person这个类就已经有了一个叫做eat(name: String)方法了。

伴随对象的扩展

格式:fun +类名+"."+"Companion"+"."+"方法名"+"(参数列表)"+"{}"

fun Person7.Companion.test2(){

}
数据对象

java中有个javaBean的概念,在kotlin中有自己的定义方法使用data修饰的的class类

data class Source(var id: String = "1",var packageName: String = "com.wifi.cool",var adPositionId: String = "1",var adCenterId: Int = 23,var level: Int =2,var centerName: String = "xxxx",var size: String = "RECT",var type: String = "DO"): Serializable

上面有如下几个特点:

  1. 参数需要有var或val修饰,可以转换成java代码对比。
  2. 参数有默认值
  3. 实现了Serializable接口
内部类

类可以标记为 inner 这样就可以访问外部类的成员。内部类拥有外部类的一个对象 引用:

class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo(): Int{ return bar}
    }
}

匿名内部类

使用object关键字

window.addMouseListener(object: MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
// ...
}
    override fun mouseEntered(e: MouseEvent) {
        // ...
} })
代理属性

代理属性的意思我理解为你的属性交给我来管理。什么意思呢?

class User(){
var name: String  = ""
var age: Int = 23
}
val  user = User()
user.name = "yhai"
user.age = 27

上面这个User对象的name和age属性的值发生改变时,我要把这个值通过SharePerference存着本地。那我就可以把这个属性代理起来,保存在本地的这个事情就在这个代理类中实现。这个类需要继承ReadWriteProperty<Any?, T>类。下面这个代理类是用来操作sp的。


import android.content.Context
import manage.ad.yiba.com.admanage.App
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

/**
 * Created by yh on 8/25/17.
 */
class Preference<T>(context: Context = App.instance, val name: String?, val default: T) : ReadWriteProperty<Any?, T> {

    val prefs by lazy {
       context.getSharedPreferences("yiba",Context.MODE_PRIVATE)
    }
    override fun getValue(thisRef: Any?, property: KProperty<*>): T = with(prefs){
        when (default) {
            is Long -> {
                getLong(name, default)
            }
            is String -> {
                getString(name, default)
            }
            is Float -> {
                getFloat(name, default)
            }
            is Int -> {
                getInt(name, default)
            }
            is Boolean -> {
                getBoolean(name, default)
            }
            else -> {
                throw IllegalArgumentException("This type can be saved into Preferences")
            }
        } as T
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = with(prefs.edit()){
        when (value) {
            is Long -> putLong(name, value)
            is String -> putString(name, value)
            is Float -> putFloat(name, value)
            is Int -> putInt(name, value)
            is Boolean -> putBoolean(name, value)
            else -> {
                throw  IllegalArgumentException("存储的类型不存在,请确认储存类型")
            }
        }.apply()
    }

}

实际上就是代理这个属性的get和set方法。
上面有两个东西没有提到:

  1. is 相当于java中的instanceof
  2. as 相当于java的强制,例如:
    java
TextView  tv = (TextView)findViewById(id);

kotlin

TextView  tv =findViewById(id) as TextView
  1. with(obj) 相当于在obj的内部。
val user = User()
with(user){
  //在这里可以直接操作User的属性和方法。而不需要user.name或者user.eat("xxxx"),省略user.
}
函数

之前对已经讲了怎么定义一个函数,但是在kotlin中函数作为一等公民,显示还有很多高级的用法。
1.单表达式函数
当函数只返回单个表达式时,大括号可以省略并在 = 后面定义函数体

fun test(){
return  23
}

可以写成下面

fun test() =23
中缀符号

在满足以下条件时,函数也可以通过中缀符号进行调用:
它们是成员函数或者是扩展函数 只有一个参数 使用 infix 关键词进行标 记

//给 Int 定义一个扩展方法
infix fun Int.shl(x: Int): Int { ...
}
1 shl 2 //用中缀注解调用扩展函数 
1.shl(2)

这个在我第一次看的时候,感觉屌爆了。

默认参数
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() ) {
...
}

注:函数如果没有返回值的时候是一个Unit类型,默认可以不写,例如上面的函数也可以写成

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() )  : Unit{
...
}
变长参数
fun asList<T>(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts)
        result.add(t)
    return result
}

使用如下:

 val list = asList(1, 2, 3)

java中使用的是...表示可变参数,kotlin中使用的是vararg。

函数内部定义函数

什么优缺点现在我也没有搞懂。

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
dfs(v) }
    dfs(graph.vertices[0])
}
函数可以赋值给另一个函数
//定义了一类
class Person6
//给该类扩展了一个方法
fun Person6.eat(name: String){

}
//创建Person6的对象
val v = Person6()
//将v.eat(xxx: String)赋值给另一个函数
fun test(xx: String) = v.eat(xx)

闭包

private val value = {xx: String,bb: String->Unit

}

上面的代码,定义了一个闭包,并且把这个闭包赋值给了一个value的变量。
怎么使用它了,有两种方式:

//第一种方法使用invoke,后面括号是参数
value.invoke("value0","value1")
//第二种方法类似于函数的调用
value("value0","value1")

闭包实际上就是一个使用{}包起来的代码块,在上面这个闭包中,->左边是闭包的参数列表,右边是返回类型。在闭包的最后一行作为默认的返回值。

{xx: String,bb: String->Unit
  {
   //闭包体
  }
}

闭包可以作为一个函数的参数如下:
test方法有一个闭包作为参数,这个闭包的参数列表为空,返回值为Unit

fun test(code: ()->Unit){
    code.invoke()
}
//调用写成下面这样
val clourse = { 

}
test(clourse)
 //在可以写成
test({})
//如果一个函数的参数只有一个闭包参数可以省略()
test{xx: String ->{
  }
}

//这个闭包体可以省略,这个以闭包作为参数的函数可以写成下面这样。
test{
}

自定义dsl

这个知识点不理解不影响android的开发
在我们的gradle中,有类型与下面的代码,他是怎么实现的呢?

  email{
        address = "1454025171@qq.com"

        baseInfo()
        from("this is  from")
        to("this is to fun")
        body{
            p("this is body fun")
        }

    }

dsl代码实现

下面这段代码是网上的一个哥们实现的,我忘了保存其地址!!


class EmailSpec{
    var address: String  = ""

    fun baseInfo() = println("address = $address")
    fun from(from: String) = println("From: $from")
    fun to(to: String) = println("To: $to")
    fun subject(subject: String) = println("Subject: $subject")
    fun body(init: BodySpec.() -> Unit): BodySpec { //这个方法的参数为一个函数,该参数函数返回Unit。该方法返回BodySpec
        val body = BodySpec()
        body.init()
        return body
    }
}


class BodySpec {
    fun p(p: String) = println("P: $p")
}

/**
 * 1. 定义一个方法函数作为参数的方法
 * 2. 对EmailSpec 扩展了一个方法,该方法没有参数,没有返回值;目的是可以调用内部的其他方法
 * 3. 该方法返回一个EmailSpec 的对象
 *
 */

fun email(init: EmailSpec.()->Unit) {
    val email = EmailSpec() //创建一个对象
    email.init()
    //调用的是参数中定义的init函数,不是系统的init函数
}

上面这个dsl理解了email这个方法,就理解了怎么定义dsl。
首先定义一个反复,这个方法有一个参数,这个参数是对EmailSpec类扩展了一个空函数,返回值也为空。在这个email方法的体中,创建了EmailSpec的实例,然后这个实例调用了这个扩展方法。EmailSpec.()->Unit 这个扩展方法是在方法中定义的,所以他的范围也只在这个email方法内部区域。

关于操作符的使用

关于操作符,有点想Rxjava里面的操作符的用法。
关于操作符的使用

那些东西没有说:
对象表达式和声明,代理类,异常,this表达式,运算符重载 ...

kotlin与java的对比

copy的内容

java 有的而 kotlin 没有

异常检查
原始类型不是类
静态成员
非私有成员
通配符类型

kotlin 有的而 java 没有

字面函数+内联函数=高性能自定义控制结构 扩展函数 空安全 智能转换 String 模板 性能 一级构造函数 First-class delegation 变量和属性类型的类型接口 单 例模式 变量推断和类型预测 范围表达式 运算符重载 伴随对象

本来准备一次性写完,太累了,分成两篇来写!!!下一篇将如何写一个android项目,如果你有使用java开发android经验,那将会是非常的容易。
下一篇将如下类容

kotlin 开发android项目的相关经验

anko相关的知识点 布局和其他
网络请求
基础类的扩展
定义组合式控件
点击事件,

kotlin 语法相关资料

kotlin 官方原文资料

在学习go语言的时候,有多返回值,在kotlin中,可能用解构声明比较
解构声明

关于操作符的使用

kotlin for android

kotlin for android

kotlin 资料大全

kotlin基础资料

什么是内联函数

kotlin和java的对比文档

关于dsl的资料

这篇文章让我读出了闭包的味道,Kotlin语法基础,函数与闭包

//闭包也是函数,是一个可以读取其他函数内部变量的函数。

推荐阅读更多精彩内容