Java:“命名参数”和“默认参数值”

函数可读性

我们关注下一面例子中的函数可读性。这是一个打印任意集合内容的函数。

/*Java的集合都有一个默认的toString实现,但是它格式化输出是固定的,而且往往不是你需要的样子*/
public static <T> String joinToString(Collection<T> collection, String separator, String prefix, String postfix){ ...}

此函数看起来超级简单,这是一个我们常用的工具类中的一个函数。我们来看看joinToString的调用:

/*Java*/
joinToString(collection, ", ", ", ", ".");

你能看出这些String都对应的是什么参数吗?这个集合的元素是用空格还是点号来分割?如果你不去查看函数的声明,我们很难回答这个问题。或许你记住了这些声明,又或许你可以借助你的IDE,但从调用代码来看,这依然很隐晦。

对于Boolean类型的标志,这个问题尤其明显。为解决这个问题,一些Java编程风格推荐创建enum类型而不是采用Boolean;而另外一些风格,会要求你通过添加注释,在注释中指明参数的名称,像这个样子:

/*Java*/
joinToString(collection, /*separator*/", ", /*prefix*/", ", /*postfix*/".");

通过指明参数名称,函数的可读性增强了,但依然不是很理想。

命名参数

Kotlin等一些语言调用函数时可以显示的指明一些参数,看起来非常优雅,而且可读性非常好,调用如下:

joinToString(collection, separator = " ", prefix = " ", postfix = ".")

你是不是看到这样的代码比看到Java代码顺眼很多呢?这行代码比起Java调用可读性确实非常优秀。
我们再讨论一个问题。

默认参数值

Java的另一个普遍存在的问题是,一些类的重载函数实在太多了。只要看一眼java.lang.Thread以及它对应的8个构造方法就让人受够了!这些重载,原本是为了向后兼容,方便这些API的使用者,又或者处于别的原因,但导致的最终结果是一致的:重复。这些参数名和类型被重复了一遍又一遍,如果你是一个良好公民,还必须在每次重载的时候重复大部分的文档。与此同时,当你省略部分参数的重载函数时,你可能搞不清楚他们到底用的是哪个。
在一些语言中,如Kotlin,可以在声明函数的时候指定参数的默认值,避免了创建重载的函数。我们看一下Kotlin中joinToString是如何声明的:

/*separator ,prifix和postfix是有默认值的参数*/
/*常用情况下字符串可以不加前缀或者后缀并用逗号分隔*/
fun <T> joinToString(collection:Collection<T>, 
								separator:String = ", ",
								prefix:String = " ",
								postfix:String = " "
):String {
	...
}

函数声明看起来没什么特别的,就是多了个默认值。那我们看看joinToString在Kotlin中的调用:

>>> joinToString(list)
1,2,3
>>> joinToString(list,separator = "; ") /*同 joinToString(list,"; ")*/
1; 2; 3
>>> joinToString(list,separator = "; ", prefix = "[", postfix = "]") /*joinToString(list,"; ","[", "]")*/
[1; 2; 3]
...

你是否发现并没有重载joinToString函数,却达到了重载函数的目的?而且在调用时可以指明参数名称,代码看起来非常的清晰。
我们来讨论讨论这些特性在Java中的实现。

Java中的“命名参数”和“默认参数值”

很不幸,Java中不能采用命名参数也不能采用默认参数值。相当多的Java开发人员已经习惯了函数重载,如果是良好开发者也习惯了重复文档。既然Java不支持这些特性,我们只能从编程风格上做文章来达到我们的目的。Builder Pattern 能解决以上我们的问题。

Builder Pattern

我还是以joinToString为例,用这种模式编写,我不在讲理论了,直接上干货吧。

public final class CollectionUtil {
    private CollectionUtil() {
    }

    public static <T> JoinToStringBuilder join(Collection<T> collection) {
        return new JoinToStringBuilder<>(collection);
    }

    public static final class JoinToStringBuilder<T> {
        private final Collection<T> collection;
        private String separator = ",";
        private String prefix = "";
        private String postfix = "";

        private JoinToStringBuilder(Collection<T> collection) {
            this.collection = collection;
        }

        public JoinToStringBuilder setSeparator(String separator) {
            this.separator = separator;
            return this;
        }

        public JoinToStringBuilder setPrefix(String prefix) {
            this.prefix = prefix;
            return this;
        }

        public JoinToStringBuilder setPostfix(String postfix) {
            this.postfix = postfix;
            return this;
        }

        @Override
        public String toString() {
            StringBuilder result = new StringBuilder();
            /*...*/
            return result.toString();
        }
    }
}

我去,代码看起来咋这么长呢?没办法,这是为了提高代码可读性付出的相应代价。
我们看看它的调用:

>>> CollectionUtil.join(list).toString();
1, 2, 3
>>> CollectionUtil.join(list).setSeparator("; ").toString();
1; 2; 3;
>>> CollectionUtil.join(list).setPrefix("[").setPostfix("]").toString();
[1, 2, 3]

你有没有发现代码可读性提高了呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a.erke

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值