Kotlin里的输入参数

Java基础

浏览数:102

2019-8-22

AD:资源代下载服务

本专栏的第一篇,分享下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")

上述即是命名参数,在调用处使用,形式为:参数名=参数值;
这样的入参带来了两个便利:

  1. 便于阅读,按顺序阅读代码就能知晓方法参数的含义
  2. 调用时入参的位置可以任意(调用的入参顺序和定义的入参的顺序允许不一致)

这样看起来真不错的

默认参数

定义函数时,给入参提供默认值,在调用处,如果不传入实参,则该参数使用默认值,可用于方法重载。例如对上述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公众号

作者:sugaryaruan