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

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

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

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

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

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

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

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

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

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

fun <T> T.takeIf(predicate: (T) -> Boolean): T?
fun <T> 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







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

推荐阅读更多精彩内容