-
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("只初始化一次")
"懒加载"
}
}
-
it Lambda语法
it 来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的。
// 即it不是`Kotlin`中的关键字。可用于变量名称
val it : Int = 0
//在 Lambdas 表达式中,大括号与表达式间要有空格,箭头与参数和函数体间要有空格
list.filter { it > 10 }.map { element -> element * 2 }
在短的且不嵌套的Lambdas中,建议使用it替换参数,有嵌套而已有多个参数的Lambdas中,就不能用it来替换参数,必须明确声明参数。
-
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
一个无参数的lambda表达式,允许自定义返回值
这不是个扩展函数,因此不能用对象点出来
但因为是顶级函数,所以可以在任何地方直接调用,其一般用途应该主要是这种代码:
run {
// do something
}
val result = run {
// do something
}
run、apply
fun
fun
这两个函数的共同点在于,参数都是T.()
因此在lambda中要以this来表示当前对象
不同点在于:run可以自定义返回值,apply只能返回当前对象
使用示例:
"Hello"
.run { this + " World" } // 在自身后面拼接字符串,并将结果返回
.apply { println(this) } // 打印刚才拼接好的字符串
also、let
fun
fun
这两个函数跟上面的两个功能几乎相同,可以看出let对run,also对apply
区别在于参数的lambda的参数部分变成了(T),因此要以it来表示当前对象
使用示例:
"Hello"
.let { it + " World" } // 在自身后面拼接字符串,并将结果返回
.also { println(it) } // 打印刚才拼接好的字符串
with
fun
这个函数的作用是以一个指定对象的身份去调用特定的代码,并允许自定义返回值
感觉主要是为了新写法设计的,一般用途应该是这样的:
with ("Hello") {
val str = this + " World"
println(str)
}
takeIf、takeUnless
fun
fun
这两个方法可以执行一段代码,这段代码可以返回一个布尔值来控制之后的流程
对于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