Kotlin里的输入参数
本专栏的第一篇,分享下Kotlin里输入参数的特性
命名参数
我们先来看一个需求:
把集合里每个元素用分号分隔并打印到括号里,例如(Java,Kotlin,Python,JavaScript,Ruby,Go)
我还想改变输出格式,前缀,分隔符,后缀都有可能发生改变,于是提取出参数,Java实现代码如下:
public class SeparatorUtils {
/**
*
* @param collections 集合
* @param prefix 前缀
* @param separator 分隔符
* @param postfix 后缀
* @param <T> 集合泛型
* @return 分隔后的结果
*/
public static <T> String separator(Collection<T> collections, String prefix, String separator, String postfix) {
Objects.requireNonNull(collections);
StringBuilder sb = new StringBuilder();
sb.append(prefix);
int size = collections.size();
int index = 0;
for (T t : collections) {
sb.append(t.toString());
if(index < size - 1){
sb.append(separator);
}
index ++;
}
sb.append(postfix);
return sb.toString();
}
}
private static void testSeparator(){
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Kotlin");
list.add("Python");
list.add("JavaScript");
list.add("Ruby");
list.add("Go");
String separator = SeparatorUtils.separator(list, "(", ",", ")");
System.out.println("separator result = " + separator);
}
输出结果:
separator result = (Java,Kotlin,Python,JavaScript,Ruby,Go)
这是一个完整的编码过程,来了一个需求,通过新增类和方法,在需要的地方,调用实现。需求被解决了,代码默默地在角落发挥着作用。
转眼三个月过去,团队里来了新人,他阅读到testSeparator方法;当读到SeparatorUtils.separator()方法,对于传的四个参数代表什么含义,乍看之下他并不清楚,需要点进去读实现代码或者看注释说明才确切明白。那么还有没有更易于阅读的方式呢?Kotlin的命名参数能做到。
SeparatorUtils.separator方法用Kotlin重写如下:
fun <T> joinToString(
collection: Collection<T>,
separator: String,
prefix: String,
postfix: String
): String {
val sb = StringBuffer(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) {
sb.append(separator)
}
sb.append(element)
}
sb.append(postfix)
return sb.toString()
}
调用joinToString方法可以这样
val list = arrayListOf("a", "b", "c")
val s = joinToString(list, postfix = ")", separator = ",", prefix = "(")
println("s = $s")
上述即是命名参数,在调用处使用,形式为:参数名=参数值;
这样的入参带来了两个便利:
- 便于阅读,按顺序阅读代码就能知晓方法参数的含义
- 调用时入参的位置可以任意(调用的入参顺序和定义的入参的顺序允许不一致)
这样看起来真不错的
默认参数
定义函数时,给入参提供默认值,在调用处,如果不传入实参,则该参数使用默认值,可用于方法重载。例如对上述Kotlin代码的joinToString方法改变入参
@JvmOverloads
fun <T> joinToString(
collection: Collection<T>,
separator: String = ",",
prefix: String = "(",
postfix: String = ")"
): String {
val sb = StringBuffer(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) {
sb.append(separator)
}
sb.append(element)
}
sb.append(postfix)
return sb.toString()
}
在Kotlin调用joinToString()支持如下,最后一个我们同时使用了Kotlin的命名参数和默认参数的特性。
val list = arrayListOf("Java", "Kotlin", "Python", "JavaScript", "Ruby", "Go")
val s = joinToString(list)
val s2 = joinToString(list,",")
val s3 = joinToString(list,",","[")
val s4 = joinToString(list,",","[","]")
val s5 = joinToString(list, prefix = "[", postfix = "]")
上述调用体现了方法重载,默认参数可提供方法重载的效果
上面出现的@JvmOverloads注解是用来做什么的呢?
默认参数特性,使用是有前提的:用Kotlin定义函数,并在Kotlin代码里调用该函数。因此,如果在Java文件里调用Kotlin定义的joinToString方法,默认不支持默认参数特性的,也即方法重载失效。
@JvmOverloads提供了让默认参数特性在Java环境也得到支持。原理是:kotlin代码编译成java代码时,会增加增加下面的方法,这些正是Java方法重载。代码如下:
public final class StringUtils {
public static final int count = 11;
@JvmOverloads
@NotNull
public static final String joinToString(@NotNull Collection collection, @NotNull String separator, @NotNull String prefix, @NotNull String postfix) {
Intrinsics.checkParameterIsNotNull(collection, "collection");
Intrinsics.checkParameterIsNotNull(separator, "separator");
Intrinsics.checkParameterIsNotNull(prefix, "prefix");
Intrinsics.checkParameterIsNotNull(postfix, "postfix");
StringBuffer sb = new StringBuffer(prefix);
Object element;
for(Iterator var6 = CollectionsKt.withIndex((Iterable)collection).iterator(); var6.hasNext(); sb.append(element)) {
IndexedValue var5 = (IndexedValue)var6.next();
int index = var5.component1();
element = var5.component2();
if (index > 0) {
sb.append(separator);
}
}
sb.append(postfix);
String var10000 = sb.toString();
Intrinsics.checkExpressionValueIsNotNull(var10000, "sb.toString()");
return var10000;
}
// $FF: synthetic method
// $FF: bridge method
@JvmOverloads
@NotNull
public static String joinToString$default(Collection var0, String var1, String var2, String var3, int var4, Object var5) {
if ((var4 & 2) != 0) {
var1 = ",";
}
if ((var4 & 4) != 0) {
var2 = "(";
}
if ((var4 & 8) != 0) {
var3 = ")";
}
return joinToString(var0, var1, var2, var3);
}
@JvmOverloads
@NotNull
public static final String joinToString(@NotNull Collection collection, @NotNull String separator, @NotNull String prefix) {
return joinToString$default(collection, separator, prefix, (String)null, 8, (Object)null);
}
@JvmOverloads
@NotNull
public static final String joinToString(@NotNull Collection collection, @NotNull String separator) {
return joinToString$default(collection, separator, (String)null, (String)null, 12, (Object)null);
}
@JvmOverloads
@NotNull
public static final String joinToString(@NotNull Collection collection) {
return joinToString$default(collection, (String)null, (String)null, (String)null, 14, (Object)null);
}
public static final void testExtend(@NotNull Container $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
String var1 = "call the container testExtend method";
System.out.println(var1);
}
}
可变参数
可变参数关键词:vararg(分别取variate和arguments前三个字母)
来看一个Kotlin的Collections类里的一个方法
/**
* Returns a new [ArrayList] with the given elements.
* @sample samples.collections.Collections.Lists.arrayList
*/
public fun <T> arrayListOf(vararg elements: T): ArrayList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
调用
val list = arrayListOf("a", "b", "c", "d")
arrayListOf入参数量可以任意多个
Java实现可变参数,在数据类型后面加三个点:... ,看下Java里的Arrays里的一个方法
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
展开运算符 *
把数组展开成一个一个元素。展开运算符常与可变运算符联合使用。比如这样:
val array = arrayOf("a", "b", "c")
val list = arrayListOf(*array)
我得到了一个ArrayList集合,集合里的元素是"a", "b", "c"
欢迎关注CodeThings公众号