前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >8. Kotlin 函数声明与默认参数(Default argument)

8. Kotlin 函数声明与默认参数(Default argument)

作者头像
sickworm
发布2020-03-23 17:34:49
4.2K0
发布2020-03-23 17:34:49
举报
文章被收录于专栏:sickwormsickworm

1. Java 的函数重载和烦恼

在 Java 中,当我们要实现同一种功能,但函数入参出参不一样的函数的时候,我们可以用到 Java 的函数重载功能。在 Android framework 中同样也存在大量的重载函数,以方便开发者调用。重载函数深入人心,得到大家的认可。

东西确实是好东西,但当重载函数过多的时候,代码就显得臃肿了,比如这里有一个 Toast 显示的工具类,在经过不断的功能扩展后,发展成一个拥有海量重载方法的类:

代码语言:javascript
复制
// ...

public static void show(CharSequence msg) {
    show(Toast.LENGTH_SHORT, msg, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
}

public static void showLong(CharSequence msg) {
    show(Toast.LENGTH_LONG, msg, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
}

public static void show(Activity activity, int resId) {
    show(activity, resId, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
}

public static void show(Activity activity, CharSequence msg) {
    show(Toast.LENGTH_SHORT, activity, msg, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
}

/**
 * 当msg0不为空时展示msg0,忽略msg1;否则显示msg1
 */
public static void show(Activity activity, CharSequence msg0, CharSequence msg1) {
    if (!TextUtils.isEmpty(msg0)) {
        show(Toast.LENGTH_SHORT, activity, msg0, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
    } else if (!TextUtils.isEmpty(msg1)) {
        show(Toast.LENGTH_SHORT, activity, msg1, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
    }
}

public static void show(Activity activity, int resId, int gravity) {
    show(Toast.LENGTH_SHORT, activity, resId == 0 ? null : getString(resId), gravity);
}

public static void show(int duration, Activity activity, int resId) {
    show(duration, activity, resId == 0 ? null : getString(resId), Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
}

public static void show(int duration, Activity activity, CharSequence msg) {
    show(duration, activity, msg, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
}

public static void show(final int duration, Activity activity, final CharSequence msg, final int gravity) {
    // 具体实现
}

// ...

和 get/set 方法一样,这是典型的信息密度低的代码。那么有什么办法能够更精简的表达同样的功能,不同的入参的特性呢?有的,就是默认参数特性。

2. 重载函数的替代者,默认参数

Kotlin 拥有默认参数的特性,如果用 Kotlin 实现上述 Java 代码,可以简化为:

代码语言:javascript
复制
fun show(msg: CharSequence,
        msg2: CharSequence? = null,
        context: Context = Global.getApplicationContext(),
        gravity: Int = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
        duration: Int = Toast.LENGTH_SHORT) {
    // 具体实现
}

我们看到,声明默认参数的方法很简单,只需要把可以使用默认参数的入参用“=“号给他赋值一个默认值即可。一般来说,我们会把必须提供的参数写在前面,有默认参数的入参排在后面。因为你在调用的时候,函数的入参默认是按顺序映射的,按上面的顺序排列的话,你只需要填完必须的参数即可;而如果还想提供可选参数,就继续按顺序填写。

那如果我只想提供部分可选参数,比如上面的show函数我只想提供duration参数,跳过其他可选参数呢?Kotlin 提供了这样的调用办法:

代码语言:javascript
复制
show("this is a toast");
show("this is a toast, duration = Toast.LENGTH_LONG);
show(msg = "this is a toast, duration = Toast.LENGTH_LONG);
show(duration = Toast.LENGTH_LONG, msg = "this is a toast);

我们发现,Kotlin 方法调用时,可以显式的指明对象和入参的映射关系,无需按顺序传递。注意,这个特性不分必须参数和可选参数,所有的参数都可以用这种形式指定映射。

但一般来说,我们只在可选参数时用到。还有一种应用场景是,当你觉得必须参数的值让人迷惑,想显式的告诉阅读者这个值所对应的入参时。

大家可能已经发现,很早以前,Android Studio 对没有提供名字的函数参数,已经默认显示这个参数对应的名字。 Flutter 的 Dart 语言也有默认参数特性,而且 Flutter 组件对默认参数的使用可谓是淋漓尽致。它会把一个控件所有可配置的参数都提供在构造函数中,而且把必须参数和可选参数分开。这样开发者可以很方便的看到它必须配置和可以配置的所有参数,不用再去慢慢找这个控件提供了什么设置方法。

3. 默认参数和函数重载对比

默认参数和重载函数对比,重载函数可以改变入参和出参(返回值),默认参数只可以改变入参。不过改变出参的场景实在很少,一般我们都会用不同的函数名来区分不同的返回值,比如 covertToFloatcovertToInt

其次,每一个重载函数都是一个方法,会记录在方法表,占用 Dex 的最大方法数。默认参数会生成 2 个方法,一个是全参数的方法 A,另一个方法 B 也是全参数,但比全参数方法多出来了 flag 参数,该 flag 参数用来记录哪些参数是可选参数。外部调用的时候调用的是方法 B,没有指定的可选参数将会被赋值 0 或 false 或 null,指定了的可选参数会被赋值,且对应个 flag 位会被标记。到了方法 B 内部,没有被 flag 标记的参数,会被设置为默认值,最后方法 B 调用 方法 A。Kotlin 通过这种方式,减少了重载函数可能带来过多的方法数。

Kotlin 也支持函数重载。

4. 函数声明的特性发展

如果是一开始接触的都是高级语言的同学,可能会觉得函数重载是个比较奇怪的特性:为什么这也算是一种特性?他们除了方法名是一样的,入参不一样,出参不一样,为什么要单独拿出来说呢?这是因为在 C 语言时代,方法的识别只通过函数名,所以无法做到函数重载。后来大家感受到同名函数不同参数存在的必要性,像 C++,就把方法的入参和出参都写到了符号表里。Java 的方法签名,也是包含入参和出参的。这样的语言,就具备识别重载函数的能力,所以函数重载就成为了一种新特性。

但函数重载,是一个个不同的函数,只是名字一致而已。在语义精简和代码规范有一定的缺陷。语义精简就是“更少的代码表达相同的意图”;而代码规范,因为函数重载的功能基本是相同的,更推荐的做法是函数重载只有一份实现代码,其他函数重载都补全参数,然后调用这个完整的实现代码,就像开头的 Toast 工具类代码一样。但实际上由于缺少实际约束,有些开发者会复制多份实现,填入到不同的重载函数中。可以说函数重载容易写出,“smelly”的代码。

而默认参数特性,避免了函数重载的语义精简和代码规范缺陷。

版权所有,转载请注明出处: https://cloud.tencent.com/developer/article/1602994

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年3月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Java 的函数重载和烦恼
  • 2. 重载函数的替代者,默认参数
  • 3. 默认参数和函数重载对比
  • 4. 函数声明的特性发展
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档