Kotlin里的输入参数

作者: sugaryaruan | 来源:发表于2018-03-22 15:48 被阅读9次

    本专栏的第一篇,分享下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公众号

    相关文章

      网友评论

        本文标题:Kotlin里的输入参数

        本文链接:https://www.haomeiwen.com/subject/flbaqftx.html