kotlin基本语法

函数

表达式:fun 方法名(参数名1: 数据类型, 参数名2: 数据类型): 返回值数据类型{

}

普通用法

fun test(a: Int): String {
        return "";
}

// 将表达式作为函数体,花括号可以省略
fun test1(a: Int): Int = 1;

// 函数返回无意义的值Unit,返回值可省略
fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

条件表达式if else

// else不可省略
fun test2(a: Int, b: Int): Int {
        if (a > b) {
            return a
        } else {
            return b
        }
    }

fun test3(a: Int, b: Int): Int = if (a > b) a else b

fun test8(s1: String, s2: String): Int {
      val i1 = test7(s1)
      val i2 = test7(s2)

      return if (i1 != null && i2 != null) i1 * i2 else 0
}

非空条件判断(没有java的三元运算符)

 fun test4(a: String?): String {
        return a?.length.toString() ?: "0";
    }

fun test5(file: File?): String {
     return file?.absolutePath ?: "xx";
 }

 fun test6(file: File?) {
     file ?: throw IllegalArgumentException()
 }

 fun test7(s: String?): Int? = s?.toInt() ?: 0

when表达式(java的switch)

1、必须加else
2、如果对象是class类型的,则必须用 is
3、object / companion object这种类型的,则不用加 is

class A{
}

object B{
}
// else不可省略
 fun test9(s: Any): Int = when (s) {
        "1" -> 1
        "2" -> 2
        is A -> 3
        B -> 4
        else -> 0
}

let、also、with、run、apply

fun test10(file: File?) {
    var let = file.let {
        it?.absolutePath
        it?.name
    }

	val also = file.also {
	    it?.absolutePath
	    it?.name
	    999
	}
	
	with(file) {
	    this?.absolutePath
	    [email protected](file)
	    this?.name
	}
	
	file.run {
	    this?.absolutePath
	    this?.name
	}
	
	file.apply {
	    this?.absoluteFile
	    this?.absolutePath
	    this?.name
		}
}

fun test11(file: File?): String = file.let { it?.absolutePath } ?: ""

// a b互换值
fun test12() {
	var a = 1;
	var b = 2;
	a = b.also { b = a }
}

区别:
kotlin基本语法_第1张图片

is(java的instanceOf)

fun test13(obj: Any): String? {
     return if (obj is String) obj else null
}

遍历

fun test14() {
    val list = listOf("a", "b", "c")
    for (li in list) {
        println(li)
    }

    for (index in list.indices) {
        println("$index ---${list.get(index)}")
    }

    list.forEach {

    }

    val map = mapOf("a" to "aa", "b" to "bb", 1 to "cc")
    map.forEach {
        println("${it.key} ${it.value}")
    }

    map.entries.forEach {
        println("${it.key} ${it.value}")
    }
}

in(区间)

fun test15(a: Int): Int {
        return if (a in 1..10) a else 0
}

vararg和array

1、当参数类型是array时,vararg可直接传入。
2、当参数类型是vararg时,array传入时需要在前面加上 * 号。

变量

普通变量

val 和 var

申明变量的关键字:val 和 var
区别:val表示只读,默认只有getter方法,var表示读写,默认有getter和setter方法

// ?表示可为null,kotlin里默认都不可为null
var name: String? = null
lateinit

如果不想赋值为null,可用 lateinit 关键字

lateinit var file:File

public protected private (和java一样),open

1、默认public,可省略不写
2、kotlin中的类默认是不可被扩展修改的,类似于java的final修饰,不可被继承,要想可被继承,可用open修饰符。
3、kotlin中的接口(interface)和抽象类(abstract)默认是public和open的。
4、接口和抽象类中的成员变量,在实现类或子类中必须实现

interface IDemo {
 	var text:String
    fun iTest()
}
abstract class AbsDemo {
    abstract fun absTest()
}
// 非抽象类必须用open修饰,才可被继承
open class BaseDemo  {
	// 非抽象方法必须用open修饰,才可被重写
    open fun baseTest() {

    }

    var file: File? = null
}
class Demo : IDemo, BaseDemo() {
	// 必须实现
	override var text: String = ""
        get() = field
        set(value) {
            field = value
        }

    override fun iTest() {

    }

    override fun baseTest() {
        super.baseTest()
        // 可访问父类的成员变量
        file
    }
}

5、另类创建接口实现类的方法,无需创建这个kt类,类似于内部类中的匿名内部类。

fun test() {
      val v = object : IDemo {
           override var text: String
               get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
               set(value) {}

           override fun iTest() {
               TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
           }
       }
    }

// 如果类A需要实现接口B,可以不用在A上面申明实现B
val a = object : A(),B{
	
}

// 或者我们仅仅只是需要一个对象而已,不想去创建这个kt类
fun test() {
	val obj = object {
	    var a: Int? = null
	}
	println(obj.a)
}

constructor(构造方法)

1、kotlin分为主构造器和次构造器,主构造器写在类名后面
2、如果主构造器没有被任何修饰符或者注解修饰,可省略 constructor构造器可用
3、构造器可用public protected private修饰
4、构造器参数中,可用var修饰参数,这时候参数可变成成员变量使用,类似于java中的 this.value = value;如果没有任何修饰,仅仅只是参数,不可被使用。
5、次构造器需要委托到主构造器,用 : this() 或者 : super()表示
6、如果父类的主构造器是有参构造,子类也必须传入参数
7、当是内部类时,this代表内部类对象引用,this@MainActivity代表外部类对象引用,相当于java中的MainActivity.this

public open class BaseDemo constructor(a: String) {
    open fun baseTest() {

    }

    var file: File? = null
}
class Demo @SuppressWarnings constructor(var name: String, var age: Int) : IDemo, BaseDemo(name) {
    fun test() {
        println(name)
    }

    constructor(name: String, age: Int, sex: Int) : this(name, age) {

    }

    override fun iTest() {

    }

    override fun baseTest() {
        super.baseTest()
        file
    }
}

inner(内部类)

1、kotlin的内部类必须用 inner class 描述
2、内部类可使用外部类的成员变量和方法

class Demo @SuppressWarnings constructor(var name: String, var age: Int) : IDemo, BaseDemo(name) {
    fun test() {
        println(name)
    }

    constructor(name: String, age: Int, sex: Int) : this(name, age) {

    }

    override fun iTest() {

    }

    override fun baseTest() {
        super.baseTest()
        file
    }

    inner class Demo2 constructor(var ctx: Context) : AbsDemo() {
        override fun absTest() {
            println(name)

            iTest()
            baseTest()
        }
    }
}

3、匿名内部类

t.setOnClickListener(object : View.OnClickListener {
            override fun onClick(p0: View?) {
                println("aa")
                println("bb")
            }
})

t.setOnClickListener { v ->
    println("aa")
    println("bb")
}

t.setOnTouchListener { view, motionEvent ->
    println("aa")
    println("bb")
    return@setOnTouchListener true
}

object / companion object(常量类)

1、kotlin中的常量不能直接申明在class类中,必须申明在object类或者companion object类中
2、用object必须申明类名,可单独使用,也可放在class类里使用;而companion object表示伴生类,必须伴随着class类使用
3、object和class的区别就是使用方式不同

// 获取Config实例:val config = Config()
// 获取A实例:val a = Config.A
class Config {
    object A{
        const val name:String = "xxx"
    }
}

// Config.name
object Config {
    const val name:String = "xxx"
}
// Config.name
class Config {
    companion object{
        const val name:String = "aaa"
    }
}

getter和setter

1、kotlin中的getter和setter默认是隐藏的,如果需要自定义,在方法里做一些逻辑处理,或者比如实现单例等,可自己实现
2、set() 和 get() 必须跟随着成员变量,field代表了方法上面的成员变量,是一对一的关系

class User {
    var name: String? = null
        set(value) {
            field = value
        }
        get() = field


    var age: Int? = null

        set(value) {
            field = value
        }
        get() = field

    lateinit var file:File

    constructor(name: String?, age: Int?) {
        this.name = name
        this.age = age
    }
}

扩展

Kotlin 能够扩展一个类的新功能而无需继承该类
声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀

fun String?.empty(): Boolean {
    return if (this == null || this.length <= 0) true else false
}

override var text: String = ""
    get() = field
    set(value) {
        if (value.empty()) {
            field = "xxx"
        } else {
            field = value
        }
    }

这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号前的对象)。即this代表了调用empty()方法的那个对象。

data(数据类)

1、我们经常创建一些只保存数据的类。 在这些类中,一些标准函数往往是从数据机械推导而来的。在 Kotlin 中,这叫做 数据类 并标记为 data
2、主构造函数需要至少有一个参数;
3、主构造函数的所有参数需要标记为 val 或 var;
4、数据类不能是抽象、开放、密封或者内部的

data class Student constructor(var name:String){

}

sealed(密封类)

当存在判断条件时,在java中一般使用的是if else,或者switch,在kotlin中,或者使用when,这时候必须要加上else ->,是不能省略的。

或者用枚举Enum。

但是枚举存在一个限制条件,就是只能示例一个对象,而且不能携带参数。

为了解决以上问题,就出现了密封类sealed。

1、密封类用sealed表示。
2、密封类的子类必须申明在密封类内部或者同一文件中。因为密封类的构造方法是私有的。
3、密封类无法实例化。

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
    class TranslateX(val px: Float): UiOp()
    class TranslateY(val px: Float): UiOp()
}

fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
    is UiOp.TranslateX -> view.translationX = op.px 
    is UiOp.TranslateY -> view.translationY = op.px
}

out / in(泛型)

1、out 相当于java里面的
2、in 相当于java里面的

inline(内联类)

inline 就是我们常说的内联。这个关键字会在编译期间起作用。如果一个函数是 inline 的,那么编译器会在编译的时候,把这个函数复制到调用处。

比如如下代码:

// Kotlin
fun main(args: Array) {
    multiplyByTwo(5)
}
fun multiplyByTwo(num: Int) : Int {
    return num * 2
}

他进行反编译之后的等价 Java 代码如下:

// Java
public static final void main(@NotNull String[] args) {
   multiplyByTwo(5);
}

public static final int multiplyByTwo(int num) {
   return num * 2;
}

可以看到,不加 inline 的方法,编译成字节码,然后再反编译成等价 java 代码,得到的结果是一个普通的方法。这个跟我们的常识是吻合的。

但是,当我们把方法用 inline 修饰了之后,会发生什么呢?

比如如下代码中,我们把 multiplyByTwo 用 inline 参数修饰了一下:

// Kotlin
fun main(args: Array) {
    multiplyByTwo(5)
}
inline fun multiplyByTwo(num: Int) : Int {
    return num * 2
}

反编译得到的结果如下:

// Java
public static final void main(@NotNull String[] args) {
   int num$iv = 5;
   int var10000 = num$iv * 2;
}

public static final int multiplyByTwo(int num) {
   return num * 2;
}

可以看到,inline 中的方法,被复制到了调用方。这就是 inline 威力强大的地方!

by(委托)

1、类委托

interface IBy {
    fun test() : Int
}

class ByImpl : IBy {
    override fun test(): Int {
        return 1
    }
}

class ByProxy constructor(by:IBy) : IBy by by{
    override fun test(): Int {
        return 2
    }
}

println(ByProxy(ByImpl()).test())
2

2、属性委托
语法格式:
val/var <属性名>:<类型> by <表达式>

by关键字之后的表达式就是委托,属性的get()方法(以及set()方法)将被委托给这个对象的getValue()和setValue()方法。属性委托不必实现任何接口,但是必须提供getValue()函数(对于var属性,还需要setValue()函数)。

如果申明的是val,则只需要实现getValue()
如果申明的是var,则只需要实现getValue()和setValue()

class ByDelegate {
    operator fun getValue(nothing: Nothing?, property: KProperty<*>): Int {
        return 1
    }

    operator fun setValue(nothing: Nothing?, property: KProperty<*>, i: Int) {

    }
}

var a:Int by ByDelegate()

高阶函数和Lambda表达式

1.函数类型,高阶函数,Lambda,它们分别是什么?

1.1 函数类型是什么?

顾名思义:函数类型,就是函数的类型。

//         (Int,  Int) ->Float 
//           ↑      ↑      ↑
fun add(a: Int, b: Int): Float { return (a+b).toFloat() }
将函数的 参数类型 和 返回值类型 抽象出来后,就得到了 函数类型。
(Int, Int) -> Float 就代表了参数类型是 两个 Int 返回值类型为 Float 的函数类型。
1.2 高阶函数是什么?

高阶函数是将函数用作参数或返回值的函数。

上面的话有点绕,直接看例子吧。如果将 Android 里点击事件的监听用 Kotlin 来实现,它就是一个典型的高阶函数。

//                      函数作为参数的高阶函数
//                              ↓
fun setOnClickListener(l: (View) -> Unit) { ... }
1.3 Lambda 是什么?

Lambda 可以理解为函数的简写。

fun onClick(v: View): Unit { ... }
setOnClickListener(::onClick)

// 用 Lambda 表达式来替代函数引用
setOnClickListener({v: View -> ...})

看到这,如果你没有疑惑,那恭喜你,这说明你的悟性很高,或者说你基础很好;如果你感觉有点懵,那也很正常,请看后面详细的解释。

2. 为什么要引入 Lambda 和 高阶函数?

刚接触到高阶函数和 Lambda 的时候,我就一直有个疑问:为什么要引入 Lambda 和 高阶函数?这个问题,官方文档里没有解答,因此我只能自己去寻找。

2.1 Lambda 和 高阶函数解决了什么问题?

这个问题站在语言的设计者角度会更明了,让我们看个实际的例子,这是 Android 中的 View 定义,我省略了大部分代码:

// View.java
private OnClickListener mOnClickListener;
private OnContextClickListener mOnContextClickListener;

// 监听手指点击事件
public void setOnClickListener(OnClickListener l) {
    mOnClickListener = l;
}

// 为传递这个点击事件,专门定义了一个接口
public interface OnClickListener {
    void onClick(View v);
}

// 监听鼠标点击事件
public void setOnContextClickListener(OnContextClickListener l) {
    getListenerInfo().mOnContextClickListener = l;
}

// 为传递这个鼠标点击事件,专门定义了一个接口
public interface OnContextClickListener {
    boolean onContextClick(View v);
}

Android 中设置点击事件和鼠标点击事件,分别是这样写的:

// 设置手指点击事件
image.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        gotoPreview();
    }
});

// 设置鼠标点击事件
image.setOnContextClickListener(new View.OnContextClickListener() {
    @Override
    public void onContextClick(View v) {
        gotoPreview();
    }
});

请问各位小伙伴有没有觉得这样的代码很啰嗦?

现在我们假装自己是语言设计者,让我们先看看上面的代码存在哪些问题:

定义方:每增加一个方法,就要新增一个接口:OnClickListener,OnContextClickListener
调用方:需要写一堆的匿名内部类,啰嗦,繁琐,毫无重点

仔细看上面的代码,开发者关心的其实只有一行代码:

gotoPreview();

如果将其中的核心逻辑抽出来,这样子才是最简明的:

image.setOnClickListener { gotoPreview() }
image.setOnContextClickListener { gotoPreview() }

Kotlin 语言的设计者是怎么做的?是这样:

1、用函数类型替代接口定义

与上面 View.java 的等价 Kotlin 代码如下:

//View.kt
var mOnClickListener: ((View) -> Unit)? = null
var mOnContextClickListener: ((View) -> Unit)? = null

fun setOnClickListener(l: (View) -> Unit) {
    mOnClickListener = l;
}

fun setOnContextClickListener(l: (View) -> Unit) {
    mOnContextClickListener = l;
}

2、用 Lambda 表达式作为函数参数

image.setOnClickListener{v -> 
	gotoPreview();
}

以上做法有以下的好处:

定义方:减少了两个接口类的定义
调用方:代码更加简明

细心的小伙伴可能已经发现了一个问题:Android 并没有提供 View.java 的 Kotlin 实现,为什么我们 Demo 里面可以用 Lambda 来简化事件监听?

// 在实际开发中,我们经常使用这种简化方式
setOnClickListener { gotoPreview() }

原因是这样的:由于 OnClickListener 符合 SAM 转换的要求,因此编译器自动帮我们做了一层转换,让我们可以用 Lambda 表达式来简化我们的函数调用。

那么,SAM 又是个什么鬼?

2.2 SAM 转换(Single Abstract Method Conversions)

SAM(Single Abstract Method),顾名思义,就是:只有一个抽象方法的类或者接口,但在 Kotlin 和 Java8 里,SAM 代表着:只有一个抽象方法的接口。符合 SAM 要求的接口,编译器就能进行 SAM 转换:让我们可以用 Lambda 表达式来简写接口类的参数。

注:Java8 中的 SAM 有明确的名称叫做:函数式接口(FunctionalInterface)。

FunctionalInterface 的限制如下,缺一不可:

必须是接口,抽象类不行
该接口有且仅有一个抽象的方法,抽象方法个数必须是1,默认实现的方法可以有多个。
也就是说,对于 View.java 来说,它虽然是 Java 代码,但 Kotlin 编译器知道它的参数 OnClickListener 符合 SAM 转换的条件,所以会自动做以下转换:

转换前:

public void setOnClickListener(OnClickListener l)

转换后:

fun setOnClickListener(l: (View) -> Unit)
// 实际上是这样:
fun setOnClickListener(l: ((View!) -> Unit)?)

((View!) -> Unit)?代表,这个参数可能为空。

2.3 Lambda 表达式引发的8种写法

当 Lambda 表达式作为函数参数的时候,有些情形下是可以简写的,这时候可以让我们的代码看起来更简洁。然而,大部分初学者对此也比较头疼,同样的代码,能有 8 种不同的写法,确实也挺懵的。

要理解 Lambda 表达式的简写逻辑,其实很简单,那就是:多写。

各位小伙伴可以跟着我接下来的流程来一起写一写:

2.3.1 第1种写法

这是原始代码,它的本质是用 object 关键字定义了一个匿名内部类:

image.setOnClickListener(object: View.OnClickListener {
    override fun onClick(v: View?) {
        gotoPreview(v)
    }
})
2.3.2 第2种写法

如果我们删掉 object 关键字,它就是 Lambda 表达式了,因此它里面 override 的方法也要跟着删掉:

image.setOnClickListener(View.OnClickListener { view: View? ->
    gotoPreview(view)
})

上面的 View.OnClickListener 被称为: SAM Constructor—— SAM 构造器,它是编译器为我们生成的。Kotlin 允许我们通过这种方式来定义 Lambda 表达式。

思考题:
这时候,View.OnClickListener {} 在语义上是 Lambda 表达式,但在语法层面还是匿名内部类。这句话对不对?

2.3.3 第3种写法

由于 Kotlin 的 Lambda 表达式是不需要 SAM Constructor的,所以它也可以被删掉。

image.setOnClickListener({ view: View? ->
    gotoPreview(view)
})
2.3.4 第4种写法

由于 Kotlin 支持类型推导,所以 View? 可以被删掉:

image.setOnClickListener({ view ->
    gotoPreview(view)
})
2.3.5 第5种写法

当 Kotlin Lambda 表达式只有一个参数的时候,它可以被写成 it。

image.setOnClickListener({ it ->
    gotoPreview(it)
})
2.3.6 第6种写法

Kotlin Lambda 的 it 是可以被省略的:

image.setOnClickListener({
    gotoPreview(it)
})
2.3.7 第7种写法

当 Kotlin Lambda 作为函数的最后一个参数时,Lambda 可以被挪到外面:

image.setOnClickListener() {
    gotoPreview(it)
}
2.3.8 第8种写法

当 Kotlin 只有一个 Lambda 作为函数参数时,() 可以被省略:

image.setOnClickListener {
    gotoPreview(it)
}

按照这个流程,在 IDE 里多写几遍,你自然就会理解了。一定要写,看文章是记不住的。

2.4 函数类型,高阶函数,Lambda表达式三者之间的关系

将函数的参数类型和返回值类型抽象出来后,就得到了函数类型。

(View) -> Unit 就代表了参数类型是 View ,返回值类型为 Unit 的函数类型。

如果一个函数的参数或者返回值的类型是函数类型,那这个函数就是高阶函数。很明显,我们刚刚就写了一个高阶函数,只是它比较简单而已。
Lambda 就是函数的一种简写

2.5 现在我们通过扩展来实现一个高阶函数
fun View.setOnClickListener(l: (View) -> Unit) {
    l.invoke(this)
}

使用:

val view = View(this@MainActivity)
view.setOnClickListener {
      startActivity()
}

假设有多个参数怎么办

fun View.setOnClickListeners(test:Int,l: (View) -> Boolean) {
    l.invoke(this)
}

// 多个高阶函数
fun View.setOnClickListeners(test: Int, l: (View) -> Boolean,l2: (View) -> Boolean) {
    this.let { l.invoke(this) }
}

还是一样的道理,将函数类型的函数单独放在花括号里:

// 写法1
val view = View(this@MainActivity)
view.setOnClickListeners(0, {
      println("-----------$it")
      return@setOnClickListeners true
})

// 写法2
val view = View(this@MainActivity)
view.setOnClickListeners(0) {
      println("-----------$it")
      return@setOnClickListeners true
}

// 多个高阶函数
val view = View(this@MainActivity)
view.setOnClickListeners(0, {
    println("-----------$it")
    return@setOnClickListeners true
}, {

    return@setOnClickListeners false
})
2.6 回调里的printIn为什么会被执行

kotlin基本语法_第2张图片

在这里插入图片描述
看图,大家知道kotlin中任何东西都是一个对象
那么图2中阴影部分的代码块其实就是一个对象,对象的引用其实就是图1中的 l1,那么要想图2的回调被执行,就需要图1中的l11去执行。即:

fun View.setOnClickListeners(l1: (View) -> Boolean, l2: View.() -> Boolean) {
    l1(this)
// 或者
    l1.invoke(this)
}

大家看到这里,会有疑问,为什么要传个this呢?是因为l1是函数类型参数,接收的是扩展对象参数,返回的是Boolean类型。而扩展对象参数View就是调用该扩展方法的调用者,这是在讲kotlin扩展函数中明确的。

也就是说view.setOnClickListeners中的view对象会传到扩展函数中的this,这个对象又作为函数类型参数的接收值回调到view.setOnClickListeners中去:

 view.setOnClickListeners({ v ->
      println("----111-------$v")
      return@setOnClickListeners true
}

这个v就是回调回来的view对象,也可以这么写:
在这里插入图片描述
看到as的智能提示,这个对象用 it 表示,所以写 v 报错了,正确的是:

view.setOnClickListeners({
      println("----111-------$it")
      return@setOnClickListeners true
}

如果函数类型中接收参数是多个怎么办,比如:
kotlin基本语法_第3张图片
这时候就不能简写lambda表达式了,会报错,也就没了 it
在这里插入图片描述
所以:it 用于 函数类型中: 函数只有一个参数 。 it表示 参数对象,并且回调返回

3. 带接收者(Receiver)的函数类型:A.(B,C) -> D

为什么要引出这个东西呢?

大家看上面的扩展函数中,l1后面还有个函数类型的参数,l2

在这里插入图片描述
发现 l1 和 l2 的区别了吗,函数类型申明方式不同

我们在定义高阶扩展函数的时候 ,某个参数是函数类型,假如想要把 扩展对象传递给 这个函数类型 。可以通过 两种方式 定义 函数类型

1、f: (T) -> Unit , 函数类型的参数为 扩展对象类型。
2、f: T.() -> Unit ,函数类型为带接收者的函数类型,接收者和扩展对象一致

先不考虑这两者的区别

要想 l2 执行,应该怎么做呢?直接看代码:
kotlin基本语法_第4张图片
看出区别了吗

由上面可以知道:
1、{ }花括号里调用必须加(),()里调用直接调用对象引用,无需加()。
2、l1 是不带接受者的,也就是 l1 不被view对象持有,所以不能通过this调用 l1 ,l1要想执行,得自己去执行,而 l2是带接受者的,被view对象持有,而view对象又可以用this表示,即可以用this去调用 l2,也可以 l2 自己执行。

看到这大家可能疑问,那都自己执行自己不就行了吗,其实真正的区别不是体现在这里,而是体现在调用方的回调里:

我先把 l1的第二个参数去掉

kotlin基本语法_第5张图片
在这里插入图片描述
所以:this 用于 带接受者的函数类型中,表示接受者,接收者和扩展对象一致,没有回调返回

虽然他们的对象地址都一样,但是描述的意义不一样。
kotlin基本语法_第6张图片
如果将 l2 的返回值类型Boolean换成Unit,apply还可以简写:
kotlin基本语法_第7张图片
如果接收者不和扩展对象一致,会怎么样呢
kotlin基本语法_第8张图片
this就调用不了 l2

kotlin基本语法_第9张图片
kotlin基本语法_第10张图片
调用方的this就不再是调用方的对象

如果在此基础上,给 l2加个传入参数呢?
kotlin基本语法_第11张图片
调用方:
kotlin基本语法_第12张图片
这里怎么会同时有个 this 和 it呢,前面已经说过了,this代表了函数类型接受者,it
代表了唯一一个函数类型的传入参数,当然,也可以这么写:
kotlin基本语法_第13张图片
结果显而易见:
kotlin基本语法_第14张图片

你可能感兴趣的:(kotlin)