还记得刚开始在正式项目上用Kotlin写代码时,很多代码是直接复制Java过来转成Kotlin的,结果代码Review的时候被评论成是用Java的思维写Kotlin代码,完全没有利用到Kotlin的特性。那我们怎么知道Java的代码如何用Kotlin特性来重写呢?别担心,本文记录并从源码角度剖析笔者在实际项目中那些Kotlin范的正确代码,帮助大家少走弯路。
当然笔者更建议多看看官方文档,多看看源码,多看看Kotlin编译后的Java代码来加深理解,这样才是真正的掌握Kotlin。毕竟,纸上得来终觉浅,绝知此事要躬行!如果不知甚解,有时候甚至可能还会弄巧成拙。
判断对象是为null,如果不为null调用对象的方法。
Java写法:
private Handler handler;
@Test
void testIfNull() {
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
Kotlin写法:
private val handler: Handler? = null
@Test
fun testIfNull(){
handler?.removeCallbacksAndMessages(null)
}
判断对象是为null,如果不为null则初始化。
Java写法:
@Test
void testIfNullInit() {
if (handler != null) {
handler = new Handler();
}
}
Kotlin写法:
@Test
fun testIfNullInit() {
handler = handler ?: Handler()
}
判断对象为null怎样,不为null怎样。
Java写法:
@Test
void testIfElse() {
if (handler != null) {
System.out.println("handler not null ");
} else {
System.out.println("handler null ");
}
}
Kotlin写法:
handler?.apply {
println("handler not null 1")
} ?: apply {
println("handler null 1")
}
handler?.apply {
println("handler not null 2 ")
} ?: let {
println("handler null 2")
}
handler?.apply {
println("handler not null 3")
} ?: run {
println("handler null 3")
}
注意,网上有些文章的写法是这样的:
handler?.let {
println("handler not null 4")
} ?: let {
println("handler null 4")
}
这样是不对的,我们看let的源码:
@kotlin.internal.InlineOnly
public inline fun T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
let返回的是block(this),这个是函数的返回值是有可能为null的,这样一来,上面的写法中?: let会因为?.let返回null导致执行的。我们测试看看:
handler = handler ?: Handler()
handler?.let {
println("handler not null 4")
null
} ?: let {
println("handler null 4")
}
结果是:
handler not null 4
handler null 4
但为什么用?.apply就不会呢?我们看apply源码:
@kotlin.internal.InlineOnly
public inline fun T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
apply返回的是this,也就是T自身,而?.apply是在T不为null的情况下才会执行apply的代码,因此?:右边就不被满足了,也就是说T为null执行?:后面的代码,不为null执行?.apply的代码。
switch配合枚举。
Java代码:
private enum WeekDays {
SUNDAY(0), MONDAY(1), TUESDAY(2), WEDNESDAY(3),
THURSDAY(4), FRIDAY(5), SATURDAY(6);
private int value;
WeekDays(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
WeekDays today = WeekDays.SUNDAY;
public void setToday(WeekDays today) {
this.today = today;
}
public WeekDays getToday() {
return today;
}
@Test
void testEnum() {
switch (today) {
case SUNDAY:
break;
case MONDAY:
break;
case TUESDAY:
break;
case WEDNESDAY:
break;
case THURSDAY:
break;
case FRIDAY:
break;
case SATURDAY:
break;
default:
break;
}
}
或者使用IntDef:
//先定义 常量
public static final int SUNDAY = 0;
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
//定义@WeekDays注解
@IntDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
@Retention(RetentionPolicy.SOURCE)
public @interface WeekDays {
}
//声明变量,限制变量的取值范围
@WeekDays
int currentDay = SUNDAY;
//添加@WeekDays注解,限制传入值的范围
public void setCurrentDay(@WeekDays int currentDay) {
this.currentDay = currentDay;
}
//添加@WeekDays注解,限制返回值的范围
@WeekDays
public int getCurrentDay() {
return currentDay;
}
@Test
void testEnum() {
// 声明变量,限制变量的取值范围
@WeekDays int today = getCurrentDay();
switch (today) {
case SUNDAY:
break;
case MONDAY:
break;
case TUESDAY:
break;
case WEDNESDAY:
break;
case THURSDAY:
break;
case FRIDAY:
break;
case SATURDAY:
break;
default:
break;
}
setCurrentDay(SUNDAY);
}
Kotlin可以使用枚举类:
/**
* 密封类
*/
sealed class WeekDays(value: Int) {
object SUNDAY : WeekDays(0)
object MONDAY : WeekDays(1)
object TUESDAY : WeekDays(2)
object WEDNESDAY : WeekDays(3)
object THURSDAY : WeekDays(4)
object FRIDAY : WeekDays(5)
object SATURDAY : WeekDays(6)
}
private var today: WeekDays = WeekDays.SUNDAY
@Test
fun testEnum() {
when (today) {
WeekDays.FRIDAY -> TODO()
WeekDays.MONDAY -> TODO()
WeekDays.SATURDAY -> TODO()
WeekDays.SUNDAY -> TODO()
WeekDays.THURSDAY -> TODO()
WeekDays.TUESDAY -> TODO()
WeekDays.WEDNESDAY -> TODO()
}
}
使用密封类比枚举好的地方在于使用when时编译器会提示我们有遗漏的分支没有处理。
看以下密封类编译成的Java代码:
public abstract static class WeekDays {
private WeekDays(int value) {
}
// $FF: synthetic method
public WeekDays(int value, DefaultConstructorMarker $constructor_marker) {
this(value);
}
public static final class SUNDAY extends KotlinUnitTest.WeekDays {
@NotNull
public static final KotlinUnitTest.WeekDays.SUNDAY INSTANCE;
private SUNDAY() {
super(0, (DefaultConstructorMarker)null);
}
static {
KotlinUnitTest.WeekDays.SUNDAY var0 = new KotlinUnitTest.WeekDays.SUNDAY();
INSTANCE = var0;
}
}
}
@Test
public final void testEnum() {
KotlinUnitTest.WeekDays var1 = this.today;
if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.FRIDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.MONDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.SATURDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.SUNDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.THURSDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.TUESDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else if (Intrinsics.areEqual(var1, KotlinUnitTest.WeekDays.WEDNESDAY.INSTANCE)) {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
} else {
throw new NoWhenBranchMatchedException();
}
}
不就是if else吗?不过编译器帮我们完成了这部分繁琐的工作,就说香不香吧。
构造这模式我们很熟悉了,AlertDialog就是用到了这个模式,我们看下精简过后的源码:
public class AlertDialog extends Dialog implements DialogInterface {
private final String title;
private final String message;
private CharSequence mPositiveButtonText;
private DialogInterface.OnClickListener mPositiveButtonListener;
private CharSequence mNegativeButtonText;
private DialogInterface.OnClickListener mNegativeButtonListener;
public AlertDialog(@NonNull Context context, String title, String message) {
super(context);
this.title = title;
this.message = message;
}
public CharSequence getPositiveButtonText() {
return mPositiveButtonText;
}
public void setPositiveButtonText(CharSequence mPositiveButtonText) {
this.mPositiveButtonText = mPositiveButtonText;
}
public OnClickListener getPositiveButtonListener() {
return mPositiveButtonListener;
}
public void setPositiveButtonListener(OnClickListener mPositiveButtonListener) {
this.mPositiveButtonListener = mPositiveButtonListener;
}
public CharSequence getNegativeButtonText() {
return mNegativeButtonText;
}
public void setNegativeButtonText(CharSequence mNegativeButtonText) {
this.mNegativeButtonText = mNegativeButtonText;
}
public OnClickListener getNegativeButtonListener() {
return mNegativeButtonListener;
}
public void setNegativeButtonListener(OnClickListener mNegativeButtonListener) {
this.mNegativeButtonListener = mNegativeButtonListener;
}
public static class Builder {
private final Context context;
private final String title;
private final String message;
private CharSequence mPositiveButtonText;
private DialogInterface.OnClickListener mPositiveButtonListener;
private CharSequence mNegativeButtonText;
private DialogInterface.OnClickListener mNegativeButtonListener;
public Builder(Context context, String title, String message) {
this.context = context;
this.title = title;
this.message = message;
}
public Builder setPositiveButtonListener(CharSequence text, DialogInterface.OnClickListener positiveButtonListener) {
mPositiveButtonText = text;
mPositiveButtonListener = positiveButtonListener;
return this;
}
public Builder setNegativeButtonListener(CharSequence text, DialogInterface.OnClickListener negativeButtonListener) {
mNegativeButtonText = text;
mNegativeButtonListener = negativeButtonListener;
return this;
}
public AlertDialog create() {
final AlertDialog dialog = new AlertDialog(context, title, message);
if (mPositiveButtonText != null) {
dialog.setPositiveButtonText(mPositiveButtonText);
}
if (mPositiveButtonListener != null) {
dialog.setPositiveButtonListener(mPositiveButtonListener);
}
if (mNegativeButtonText != null) {
dialog.setNegativeButtonText(mNegativeButtonText);
}
if (mNegativeButtonListener != null) {
dialog.setPositiveButtonListener(mNegativeButtonListener);
}
return dialog;
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}
如果使用Kotlin还是用Java的思维写这个代码,就相当于用Kotlin直译了一遍而已。看看Java转Kotlin后的代码:
class AlertDialog(context: Context, private val title: String, private val message: String) :
Dialog(context), DialogInterface {
var positiveButtonText: CharSequence? = null
var positiveButtonListener: DialogInterface.OnClickListener? = null
var negativeButtonText: CharSequence? = null
var negativeButtonListener: DialogInterface.OnClickListener? = null
class Builder(
private val context: Context,
private val title: String,
private val message: String
) {
private var mPositiveButtonText: CharSequence? = null
private var mPositiveButtonListener: DialogInterface.OnClickListener? = null
private var mNegativeButtonText: CharSequence? = null
private var mNegativeButtonListener: DialogInterface.OnClickListener? = null
fun setPositiveButtonListener(
text: CharSequence?,
positiveButtonListener: DialogInterface.OnClickListener?
): Builder {
mPositiveButtonText = text
mPositiveButtonListener = positiveButtonListener
return this
}
fun setNegativeButtonListener(
text: CharSequence?,
negativeButtonListener: DialogInterface.OnClickListener?
): Builder {
mNegativeButtonText = text
mNegativeButtonListener = negativeButtonListener
return this
}
fun create(): AlertDialog {
val dialog = AlertDialog(context, title, message)
if (mPositiveButtonText != null) {
dialog.positiveButtonText = mPositiveButtonText
}
if (mPositiveButtonListener != null) {
dialog.positiveButtonListener = mPositiveButtonListener
}
if (mNegativeButtonText != null) {
dialog.negativeButtonText = mNegativeButtonText
}
if (mNegativeButtonListener != null) {
dialog.positiveButtonListener = mNegativeButtonListener
}
return dialog
}
fun show(): AlertDialog {
val dialog = create()
dialog.show()
return dialog
}
}
}
没想到,Kotlin版本只有58行代码,差不多是Java的一半,Kotlin语法糖果然名不虚传。不过,上面的代码并不是一个传统的构造者模式。具体可以参考构造者设计模式。扯远了,回到正题。
那如何利用Kotlin特性写出更优雅的代码?那就是利用Kotlin Function Literals with Receiver。官方文档的介绍如下:
Function literals with receiver
Function types with receiver, such as T.() -> R, can be instantiated with a special form of function literals – function literals with receiver.
As mentioned above, Kotlin provides the ability to call an instance of a function type with receiver while providing the receiver object.
Inside the body of the function literal, the receiver object passed to a call becomes an implicit this, so that you can access the members of that receiver object without any additional qualifiers, or access the receiver object using a this expression.
This behavior is similar to that of extension functions, which also allow you to access the members of the receiver object inside the function body.
机器翻译:
带有接收器的函数字面量
带接收器的函数类型,如 T.() -> R,可以用函数字面量的特殊形式来实例化--带接收器的函数字面量。
如上所述,Kotlin提供了调用 带接收器的函数类型实例的能力,同时 提供接收器对象。
在函数字面量的主体中,传递给调用者的 接收器对象成为一个隐含的this,因此你可以访问该接收器对象的成员,而不需要任何额外的限定词,或者使用this表达式访问接收器对象。
这种行为 类似于扩展函数的行为,扩展函数也允许你在函数体中访问接收对象的成员。
可能首次接触个玩意的读者会有点不理解。首先它是一个Lamdba表达式,对Lamdba表达式不理解的可以看笔者这篇文章:深入理解Java Lambda表达式,匿名函数,闭包。不过Kotlin的Lamdba表达式和Java的Lamdba表达式并不是同一个东西,具体见扔物线大佬的Kotlin 的 Lambda 表达式,大多数人学得连皮毛都不算。
Lambda expressions and anonymous functions are function literals.
翻译:Lambda表达式和匿名函数是字面量( function literals )
来自官方文档: https://kotlinlang.org/docs/lambdas.html#lambda-expressions-and-anonymous-functions
说了这么多,这个带接收器的Lambda表达式T.() -> R有什么作用?别急,我们先理解它怎么用,举个例子:
比如实现一个两数相加的表达式,正常会这么写:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
println(sum.invoke(1, 1))
println(sum(1, 2))
我们甚至可以简化成:
val sum = { x: Int, y: Int -> x + y }
println(sum.invoke(1, 1))
println(sum(1, 2))
虽然简化了,但是基于函数式编程的思想我们可以进一步改造,这时候就可以使用写一个sum扩展函数了:
fun Int.sum(other: Int): Int {
return this + other
}
//1+2
println(1.sum(2))
Lambda表达式是理解了,那接收器怎么说?其实前面已经提到了,在函数字面量的主体中,传递给调用者的接收器对象成为一个隐含的this,因此你可以访问该接收器对象的成员,而不需要任何额外的限定词,或者使用this表达式访问接收器对象。
再看这个带接收器的Lambda表达式T.() -> R,我们拆分为两个部分来理解:
T.():
这个形式看起来像不像扩展函数,其实可以简单理解为是T这个泛型的扩展函数(本质都是获取到了T的对象,不过编译器做的工作有所不同,下面会讲到),因此我们可以直接访问T的成员,就像this那样使用。
R:
R就是接收器,接收什么?接收T对象,也就是接收器对象(即为T的对象)。注意接收器和接收器对象不是一回事。这么说吧,接收器对象(T对象)传给了接收器,在接收器内部使用this就是使用接收器对象T对象的this,前面说的隐含的this就是这么个意思。不过用不用this表达式是我们的自由,最终的效果是在接收器内部可以不需要任何额外的限定词即可访问T对象的成员。
回到上面的例子,按照T.() -> R,我们可以写出下面的代码:
val sum = fun Int.(other: Int): Int = this + other
println(1.sum(2))
println(sum(1,2))
fun Int.(other: Int): Int = this + other是不是像极了扩展函数,一样的形式,一样的使用this。
不过这个例子没有体现出接收器对象是一个隐含的this的精髓,修改下:
val sum: Int.(Int) -> Int = { other -> plus(other) }
println(1.sum(2))
println(sum(1,2))
参数里的other可以去掉,并且可以直接调用plus函数(这就是隐含this的体现),实际上就等于下面的代码:
val sum: Int.(other: Int) -> Int = { other -> this.plus(other) }
println(1.sum(2))
println(sum(1,2))
到这里,你可以观察到一个细节,如果使用扩展函数,实际使用的时候只能用下面的方式:
println(1.sum(2))
但是带接收器的Lambda表达式T.() -> R却同时支持:
println(1.sum(2))
println(sum(1,2))
再对比下它们的写法:
//扩展函数
fun Int.sum(other: Int): Int {
return this + other
}
println(1.sum(2))
//带接收器的Lambda表达式T.() -> R
val sum: Int.(other: Int) -> Int = { other -> this.plus(other) }
println(1.sum(2))
println(sum(1,2))
为什么带接收器的Lambda表达式T.() -> R支持sum(1,2)这样调用,而扩展函数却不支持?还是得看反编译看源码来理解。
先看扩展函数的:
//Kotlin源码
class KotlinLambda {
@Test
fun testLambda() {
/*val sum = { x: Int, y: Int -> x + y }
println(sum.invoke(1, 1))
println(sum(1, 2))*/
1.sum(2)
println(1.sum(2))
//println(sum(1,2))
}
}
fun Int.sum(other: Int): Int {
return this + other
}
//下面是反编译成Java
// KotlinLambda.java
public final class KotlinLambda {
@Test
public final void testLambda() {
KotlinLambdaKt.sum(1, 2);
int var1 = KotlinLambdaKt.sum(1, 2);
System.out.println(var1);
}
}
// KotlinLambdaKt.java
public final class KotlinLambdaKt {
public static final int sum(int $this$sum, int other) {
return $this$sum + other;
}
}
可以看到,多了个KotlinLambdaKt,原来的扩展函数sum变成了KotlinLambdaKt的静态函数sum。既然是个静态函数,那就肯定不能直接sum(1,2)这样调用,得 KotlinLambdaKt.sum(1, 2)这样才行。也就是说扩展函数的本质是生成了一个名为XXXKt的类,包含一个同名的静态函数,静态函数中的第一个参数,是当前调用的对象。这就是为什么扩展函数允许你在函数体中访问接收对象的成员。这并不是什么魔法,者是编译器的功劳。
所以,如果Java要调用Kotlin的扩展函数,就需要通过对应的类去调用同名的静态函数:
KotlinLambdaKt.sum(1,2);
不过再次强调,第一个参数,扩展函数T的对象,由于Int这种基本类型,所以传入值是没问题的,但是如果是自定义的类的扩展对象,那就需要传入类的实例。
再看
//Kotlin源码
class KotlinLambda {
@Test
fun testLambdaWithReceiver() {
val sum: Int.(other: Int) -> Int = { other -> this.plus(other) }
println(1.sum(2))
println(sum(1, 2))
}
}
//反编译成java
public final class KotlinLambda {
@Test
public final void testLambdaWithReceiver() {
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
}
}
这个Function2是什么?源码是这样的:
public interface Function2 : Function {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2): R
}
其实官方定义了很多的接口,从Function0到Function22,分别支持0-22个参数,具体看源码就知道了。这时候我们就知道了,Lambda表达式的声明就是这些Function接口,Lambda表达式的主体就是对应Function接口的实现。
所以带接收器的Lambda表达式T.() -> R为什么支持下面这两种形式的调用就显而易见了。
//Kotlin
println(1.sum(2))
println(sum(1,2))
//Java
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
看到了吧,1.sum(2)其实就是一个语法糖,反编译成Java,就是调用Function的接口实例的invoke方法,入参是调用对象本身和扩展函数的参数,返回值则是扩展函数的返回值。sum(1,2)也是一样的。
再对比扩展函数,它们的区别就显而易见了。
扩展函数 |
本质是静态函数,函数的第一个参数传入了调用者的对象,后面则是扩展函数声明的参数。 |
Lambda表达式 |
本质是Function的接口,接口的invoke方法传入的参数是Lambda表达式声明的参数。 |
带接收器的Lambda表达式T.() -> R |
本质是Function的接口,接口的invoke方法传入的第一个参数是调用者的对象,后面则是Lambda表达式声明的参数。所谓的接收器就是Function的invoke方法。接收器对象就是调用者T的对象。 |
附上完整的Kotlin和反编译后Java方便大家理解:
package com.nxg.composeplane
import org.junit.Test
class KotlinLambda {
var value = 0
@Test
fun testLambda() {
val sum = { x: Int, y: Int -> x + y }
println(sum.invoke(1, 1))
println(sum(1, 2))
1.sum(2)
println(1.sum(2))
println(this.sum(2))
}
@Test
fun testLambdaWithReceiver() {
val sum = fun Int.(other: Int): Int = this + other
println(1.sum(2))
println(sum(1, 2))
}
@Test
fun testLambdaWithReceiver2() {
val sum: Int.(Int) -> Int = { other -> plus(other) }
println(1.sum(2))
println(sum(1, 2))
}
@Test
fun testLambdaWithReceiver3() {
val sum: Int.(other: Int) -> Int = { other -> this.plus(other) }
println(1.sum(2))
println(sum(1, 2))
}
@Test
fun testLambdaWithReceiver4() {
val sum: KotlinLambda.(other: Int) -> Int = { other -> this.value + other }
println(sum(2))
println(sum(this, 2))
}
@Test
fun testLambdaWithReceiver5() {
val sum: (other: Int) -> Int = { other -> this.value + other }
println(sum(2))
}
}
fun Int.sum(other: Int): Int {
return this.plus(other)
}
fun KotlinLambda.sum(other: Int): Int {
return this.value + other
}
// KotlinLambda.java
package com.nxg.composeplane;
import kotlin.Metadata;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import org.junit.Test;
@Metadata(
mv = {1, 5, 1},
k = 1,
d1 = {"\u0000\u001c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0005\n\u0002\u0010\u0002\n\u0002\b\u0006\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\b\u0010\t\u001a\u00020\nH\u0007J\b\u0010\u000b\u001a\u00020\nH\u0007J\b\u0010\f\u001a\u00020\nH\u0007J\b\u0010\r\u001a\u00020\nH\u0007J\b\u0010\u000e\u001a\u00020\nH\u0007J\b\u0010\u000f\u001a\u00020\nH\u0007R\u001a\u0010\u0003\u001a\u00020\u0004X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0005\u0010\u0006\"\u0004\b\u0007\u0010\b¨\u0006\u0010"},
d2 = {"Lcom/nxg/composeplane/KotlinLambda;", "", "()V", "value", "", "getValue", "()I", "setValue", "(I)V", "testLambda", "", "testLambdaWithReceiver", "testLambdaWithReceiver2", "testLambdaWithReceiver3", "testLambdaWithReceiver4", "testLambdaWithReceiver5", "ComposerPlane.app.unitTest"}
)
public final class KotlinLambda {
private int value;
public final int getValue() {
return this.value;
}
public final void setValue(int var1) {
this.value = var1;
}
@Test
public final void testLambda() {
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(1, 1)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
KotlinLambdaKt.sum(1, 2);
var2 = KotlinLambdaKt.sum(1, 2);
System.out.println(var2);
var2 = KotlinLambdaKt.sum(this, 2);
System.out.println(var2);
}
@Test
public final void testLambdaWithReceiver() {
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
}
@Test
public final void testLambdaWithReceiver2() {
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
}
@Test
public final void testLambdaWithReceiver3() {
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(1, 2)).intValue();
System.out.println(var2);
}
@Test
public final void testLambdaWithReceiver4() {
Function2 sum = (Function2)null.INSTANCE;
int var2 = ((Number)sum.invoke(this, 2)).intValue();
System.out.println(var2);
var2 = ((Number)sum.invoke(this, 2)).intValue();
System.out.println(var2);
}
@Test
public final void testLambdaWithReceiver5() {
Function1 sum = (Function1)(new Function1() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
return this.invoke(((Number)var1).intValue());
}
public final int invoke(int other) {
return KotlinLambda.this.getValue() + other;
}
});
int var2 = ((Number)sum.invoke(2)).intValue();
System.out.println(var2);
}
}
// KotlinLambdaKt.java
package com.nxg.composeplane;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
@Metadata(
mv = {1, 5, 1},
k = 2,
d1 = {"\u0000\u000e\n\u0000\n\u0002\u0010\b\n\u0002\u0018\u0002\n\u0002\b\u0002\u001a\u0012\u0010\u0000\u001a\u00020\u0001*\u00020\u00022\u0006\u0010\u0003\u001a\u00020\u0001\u001a\u0012\u0010\u0000\u001a\u00020\u0001*\u00020\u00012\u0006\u0010\u0003\u001a\u00020\u0001¨\u0006\u0004"},
d2 = {"sum", "", "Lcom/nxg/composeplane/KotlinLambda;", "other", "ComposerPlane.app.unitTest"}
)
public final class KotlinLambdaKt {
public static final int sum(int $this$sum, int other) {
return $this$sum + other;
}
public static final int sum(@NotNull KotlinLambda $this$sum, int other) {
Intrinsics.checkNotNullParameter($this$sum, "$this$sum");
return $this$sum.getValue() + other;
}
}
再啰嗦下,可能有读者会对这个有疑问:(Function2)null.INSTANCE,为啥有个null,因为调用者是基本数据类型。如果是其它类,则是这样的: Function2 sum = (Function1)(new Function2(){});
老毛病又犯了,花了比较多的篇幅去介绍了带接收器的Lambda表达式T.() -> R,回到整体,构造者模式怎么使用这个特性来进行改造?
在AlertDialog的半生对象中定义一个内联的build方法,如下:
companion object {
inline fun build(
context: Context,
title: String,
message: String,
block: Builder.() -> Unit
) = Builder(context, title, message).apply(block)
}
关键是使用了函数apply,因为它的参数为带接收器的Lambda表达式T.() -> R,并且返回了T.this:
@kotlin.internal.InlineOnly
public inline fun T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
这样一来,Builder为T时,我们就可以在block代码块中访问Builder.this,从而简化原来链式调用的写法。示例如下:
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
AlertDialog.build(appContext, "title", "message") {
setPositiveButtonListener("confirm") { _, _ ->
}
setNegativeButtonListener("cancel") { _, _ ->
}
}.show()
不过有的读者可能会有疑问,你这种方式也没有更优雅啊?我直接通过AlertDialog().apply{}这种方式不是也可以吗?效果差不多。
AlertDialog(appContext, "title", "message").apply {
setPositiveButtonListener("confirm") { _, _ ->
}
setNegativeButtonListener("cancel") { _, _ ->
}
}
是的,包括Builder也可以直接AlertDialog.Builder().apply{}这样用。没必另外搞一个build的方法。
这里把三种写法放到一起供大家比较:
AlertDialog.build(appContext, "title", "message") {
setPositiveButtonListener("confirm") { _, _ ->
}
setNegativeButtonListener("cancel") { _, _ ->
}
}.show()
AlertDialog(appContext, "title", "message").apply {
setPositiveButtonListener("confirm") { _, _ ->
}
setNegativeButtonListener("cancel") { _, _ ->
}
}.show()
AlertDialog.Builder(appContext, "title", "message").apply {
setPositiveButtonListener("confirm") { _, _ ->
}
setNegativeButtonListener("cancel") { _, _ ->
}
}.create().show()
大家倾向于哪一种?
这里谈下笔者的理解。如果是从构造者模式的角度模式出发,AlertDialog().apply{}只是利用了apply的特性,并不是构造者模式的实现。那么问题来了,我们真的需求构造者模式吗?
答案是看情况。首先,我们得了解构造者模式的定义以及它的使用场景和优缺点。然后,再根据实际需求来决定是否需要使用构造者模式。如果构建一个对象的流程是简单的,直接apply足以,并不是非得使用构造者模式不可。
当然,如果构建一个对象的流程是复杂的,并且经常需要构造不同的对象,比如Android中的AlertDialog,那还是非常建议使用构造者模式的。
不过设计模式虽然好,但是,网上有一个观点,不要滥用设计模式。笔者对这个一观点深以为然,包括六大设计原则也是一样。如果不了解这些设计模式的出现的缘由,不了解它们的使用场景,优缺点,就很容易出现滥用甚至适得其反的情况。
最直接的一个例子就是单例模式,在项目里到处可见,而且是不考虑使用场景的。要知道单例一旦初始化,就是长期占用内存的,我们需要考虑哪些对象是可以长期存在的,哪些是可以用完就回收的。总之,更好的方式是从软件工程的思维去考虑。
另外的例子就是不看需求,生搬硬套。比如状态模式的使用,可以参考笔者的Android代码重构系列-01-Kotlin实现状态机,不同的需求实现状态机有其适合的方式,并不是非得使用状态模式。
考虑到篇幅的原因,本篇文章很难一次写完然后再发布。因此改成不定期更新。更新时会加上相应的日期。文章存在的问题也会及时修改并重新发布,欢迎交流。
写在最后,首先非常感谢您耐心阅读完整篇文章,坚持写原创且基于实战的文章不是件容易的事,如果本文刚好对您有点帮助,欢迎您给文章点赞评论,您的鼓励是笔者坚持不懈的动力。写博客不仅仅是巩固学习的一个好方式,更是一个观点碰撞查漏补缺的绝佳机会,若文章有不对之处非常欢迎指正,再次感谢。
Kotlin - 改良构建者模式
(译)Effective Kotlin之建造者模式(二)
你会用Kotlin实现构建者模式吗?