Kotlin学习(二):常用关键字

  1. by 委托模式

Kotlin中,委托的实现依靠于关键字 by ,by表示将抽象主题的实例(by后边的实例)保存在代理类实例的内部,比如SportsManager类继承于ISports接口,并可以ISports接口的所有的 public 方法委托给一个指定的对象。
类委托:

interface ISports {
    fun doSports()
}

class SwimForSports: ISports{
    override fun doSports() {
        println("do swim")
    }
}

class SportsManager(sport: ISports): ISports by sport

fun main(args: Array) {
    val swimSports: SwimForSports = SwimForSports()
    SportsManager(swimSports).doSports()// Log:do swim
}

在SportsManager声明中,by子句表示,将sport保存在SportsManager的对象实例内部,而且编译器将会生成继承自 ISports 接口的所有方法, 并将调用转发给sport。

委托属性:
所谓的委托属性,就是对其属性值的操作不再依赖于其自身的getter()/setter()方法,是将其托付给一个代理类,从而每个使用类中的该属性可以通过代理类统一管理,再也不用在每个类中,对其声明重复的操作方法。
当我们使用属性的get或者set的时候,属性委托的getValue和setValue就会被调用。

fun main(args: Array) {
    val bigHeadSon = BigHeadSon()

    bigHeadSon.money = 100//收到100块压岁钱

    println(bigHeadSon.money)//50 被代保管了一半

}

class BigHeadSon {
    var money: Int by Mother()
}

class Mother {

    var moneySon = 0
    var moneyMother = 0

    operator fun getValue(bigHeadSon: BigHeadSon, property: KProperty<*>): Int {
        return moneySon
    }

    operator fun setValue(bigHeadSon: BigHeadSon, property: KProperty<*>, i: Int) {
        moneySon = i / 2
        moneyMother = i / 2
    }
}

延迟加载(Lazy)
lazy()是一个函数, 接受一个Lambda表达式作为参数, 返回一个Lazy类型的实例,这个实例可以作为一个委托, 实现延迟加载属性(lazy property): 第一次调用 get() 时, 将会执行 lazy() 函数受到的Lambda 表达式,然后会记住这次执行的结果, 以后所有对 get() 的调用都只会简单地返回以前记住的结果.

惰性加载
by lazy 放到成员变量中,可以单独存在
by lazy 返回值就是最后一行
by lazy 是线程安全的

fun main(args: Array) {
    val lazy = Lazy()
    println(lazy.name)
    println(lazy.name)
    println(lazy.name)
}

class Lazy {
    val name: String by lazy {
        println("只初始化一次")
        "懒加载"
    }
}

  1. it Lambda语法

it 来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的。

 // 即it不是`Kotlin`中的关键字。可用于变量名称
val it : Int = 0 

//在 Lambdas 表达式中,大括号与表达式间要有空格,箭头与参数和函数体间要有空格
list.filter { it > 10 }.map { element -> element * 2 }

在短的且不嵌套的Lambdas中,建议使用it替换参数,有嵌套而已有多个参数的Lambdas中,就不能用it来替换参数,必须明确声明参数。

  1. Kotlin中let, apply, run, with, also函数

1, 『let』操作符:如果对象的值不为空,则允许执行这个方法。
场景一: 最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。

//判断object为null的操作
object?.let{//表示object不为null的条件下,才会去执行let函数体
it.todo()
}

场景二: 然后就是需要去明确一个变量所处特定的作用域范围内可以使用

另一种用途  let函数的使用的一般结构
object.let{
it.todo()//在函数体内使用it替代object对象去访问其公有的属性和方法
...
}

从源码let函数的结构来看它是只有一个lambda函数块block作为参数的函数,调用T类型对象的let函数,则该对象为函数的参数。在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。

//Java
if (currentUser != null) {
    text.setText(currentUser.name)
}

//instead Kotlin
user?.let {
    println(it.name)
}
//源码
public inline fun  T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

2,with函数的kotlin和Java转化
with函数和前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。

可以看出with函数是接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,所以with函数最原始样子如下:

//kotlin

fun main(args: Array) {
    val user = User("Kotlin", 1, "1111111")

    val result = with(user) {
        println("my name is $name, I am $age years old, my phone number is $phoneNum")
        1000
    }
    println("result: $result")
}

//java

 public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      User user = new User("Kotlin", 1, "1111111");
      String var4 = "my name is " + user.getName() + ", I am " + user.getAge() + " years old, my phone number is " + user.getPhoneNum();
      System.out.println(var4);
      int result = 1000;
      String var3 = "result: " + result;
      System.out.println(var3);
   }

//源码
public inline fun  with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

3,run函数的kotlin和Java转化
run函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式。

//kotlin

fun main(args: Array) {
    val user = User("Kotlin", 1, "1111111")

    val result = user.run {
        println("my name is $name, I am $age years old, my phone number is $phoneNum")
        1000
    }
    println("result: $result")
}

//java

  public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      User user = new User("Kotlin", 1, "1111111");
      String var5 = "my name is " + user.getName() + ", I am " + user.getAge() + " years old, my phone number is " + user.getPhoneNum();
      System.out.println(var5);
      int result = 1000;
      String var3 = "result: " + result;
      System.out.println(var3);
   }
//源码
public inline fun  T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

4,apply函数的kotlin和Java转化
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。

//kotlin

fun main(args: Array) {
    val user = User("Kotlin", 1, "1111111")

    val result = user.apply {
        println("my name is $name, I am $age years old, my phone number is $phoneNum")
        1000
    }
    println("result: $result")
}

//java

public final class ApplyFunctionKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      User user = new User("Kotlin", 1, "1111111");
      String var5 = "my name is " + user.getName() + ", I am " + user.getAge() + " years old, my phone number is " + user.getPhoneNum();
      System.out.println(var5);
      String var3 = "result: " + user;
      System.out.println(var3);
   }
}

//源码
public inline fun  T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

5, also函数编译后的class文件
also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

//kotlin

fun main(args: Array) {
    val result = "testLet".also {
        println(it.length)
        1000
    }
    println(result)
}

//java

public final class AlsoFunctionKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      String var2 = "testLet";
      int var4 = var2.length();
      System.out.println(var4);
      System.out.println(var2);
   }
}

区别总结

非扩展函数的 run

fun run(block: () -> R): R
一个无参数的lambda表达式,允许自定义返回值
这不是个扩展函数,因此不能用对象点出来
但因为是顶级函数,所以可以在任何地方直接调用,其一般用途应该主要是这种代码:

run {
    // do something
}
val result = run {
    // do something
}
run、apply

fun T.run(block: T.() -> R): R
fun T.apply(block: T.() -> Unit): T
这两个函数的共同点在于,参数都是T.()
因此在lambda中要以this来表示当前对象
不同点在于:run可以自定义返回值,apply只能返回当前对象
使用示例:

"Hello"
    .run { this + " World" } // 在自身后面拼接字符串,并将结果返回
    .apply { println(this) } // 打印刚才拼接好的字符串
also、let

fun T.let(block: (T) -> R): R
fun T.also(block: (T) -> Unit): T

这两个函数跟上面的两个功能几乎相同,可以看出let对run,also对apply
区别在于参数的lambda的参数部分变成了(T),因此要以it来表示当前对象
使用示例:

"Hello"
    .let { it + " World" } // 在自身后面拼接字符串,并将结果返回
    .also { println(it) } // 打印刚才拼接好的字符串
with

fun with(receiver: T, block: T.() -> R): R

这个函数的作用是以一个指定对象的身份去调用特定的代码,并允许自定义返回值
感觉主要是为了新写法设计的,一般用途应该是这样的:

with ("Hello") {
    val str = this + " World"
    println(str)
}
takeIf、takeUnless

fun T.takeIf(predicate: (T) -> Boolean): T?
fun T.takeUnless(predicate: (T) -> Boolean): T?

这两个方法可以执行一段代码,这段代码可以返回一个布尔值来控制之后的流程
对于takeIf:如果返回true,则这个函数会返回this,否则会返回null
而对于takeUnless则刚好与takeIf的行为相反
使用示例:

"Hello"
    .takeIf { it.length == 5 } // 判断字符串的长度等于 5,返回 true,因此 takeIf 函数会返回 this
    ?.run { this + " World!" } // 拼接字符串,因为上一句没返回 null,所以可以正常执行
    ?.apply(::println) // 打印刚刚拼好的字符串
    ?.takeUnless { it.length == 5 } // 判断字符串的长度等于 5,返回 false,因此 takeUnless 函数会返回 this
    ?.apply(::println) // 打印刚刚拼好的字符串
    ?.takeIf { it.length == 5 } // 判断字符串的长度等于 5,返回 false,因此 takeIf 函数会返回 null
    ?.apply(::println) // 这句不会执行!因为上一句返回了 null

```





文章来自:
      https://blog.csdn.net/u013064109/article/details/78786646
     https://www.jianshu.com/p/1f139e3cfb49







你可能感兴趣的:(Kotlin学习(二):常用关键字)