Talking about how to make functions better in Kotlin grammar articles (3)

https://my.oschina.net/u/3847608/blog/1837611 

Brief description: Today is the third part of the Kotlin talk series. Let’s talk about function calls and function overloading. See the title and you know that Kotlin has advantages over Java in function calls. First throw out the following pits (it is estimated that you have stepped on it before...) and see how we fill the pits step by step, from which you will experience the charm of the language of Kotlin.

  • 1. What kind of pits does Java have in function calls?
  • 2. How does Kotlin solve the pitfalls of function calls?
  • 3. What pitfalls does Java have in function overloading?
  • 4. How does Kotlin solve the pitfalls caused by function overloading?
  • 5. What problems should be paid attention to when overloading functions when Java and Kotlin call each other?

1. Problems with Java function calls

Let's try to recall that in the process of developing programs in Java, we often call some methods. Some people design methods with many parameters, and sometimes the parameter naming is not standardized (not able to meet the name and knowledge), and the design Several of the same parameter types are still next to each other. This actually brings a lot of trouble and trouble to the caller. A cautious programmer will locate the place where this function is defined, and probably look at the order of parameter calls and the function of each parameter. Especially when this method is packaged into a lib, it is troublesome to check. And the same type of parameters next to each other is easy to correspond to the wrong. Let's take a look at this example (now the requirement is: to print the elements in each set according to the prefix, separator, and suffix)

    //张三设计的函数接口顺序(调用者必须按照这个顺序传)
    static String joinToString(List<Integer> nums, String prex, String sep, String postfix) {
        StringBuilder builder = new StringBuilder(prex);
        for (int i = 0; i < nums.size(); i++) {
            builder.append(i);
            if (i < nums.size() - 1) {
                builder.append(sep);
            }
        }
        builder.append(postfix);
        return builder.toString();
    }

    //李四设计的函数接口顺序(调用者必须按照这个顺序传)
    static String joinToString(List<Integer> nums, String sep, String prex, String postfix) {
        StringBuilder builder = new StringBuilder(prex);
        for (int i = 0; i < nums.size(); i++) {
            builder.append(i);
            if (i < nums.size() - 1) {
                builder.append(sep);
            }
        }
        builder.append(postfix);
        return builder.toString();
    }

    //王二设计的函数接口顺序(调用者必须按照这个顺序传)
    static String joinToString(List<Integer> nums, String prex, String postfix, String sep) {
        StringBuilder builder = new StringBuilder(prex);
        for (int i = 0; i < nums.size(); i++) {
            builder.append(i);
            if (i < nums.size() - 1) {
                builder.append(sep);
            }
        }
        builder.append(postfix);
        return builder.toString();
    }  
    
    //假如现在叫你修改一下,拼接串前缀或分隔符,仅从外部调用是无法知道哪个参数是前缀、分隔符、后缀
    public static void main(String[] args) {
    //后面传入的三个字符串顺序很容易传错,并且外部调用者如果不看具体函数定义根本很难知道每个字符串参数的含义,特别公司中一些远古代码,可能还得打成库的代码,点进去看实现肯定蛋疼。
       //调用张三接口
        System.out.println(joinToString(Arrays.asList(new Integer[]{1, 3, 5, 7, 9}), "<", ",", ">"));
       //调用李四接口
        System.out.println(joinToString(Arrays.asList(new Integer[]{1, 3, 5, 7, 9}), ",", "<", ">"));
      //调用王二接口
        System.out.println(joinToString(Arrays.asList(new Integer[]{1, 3, 5, 7, 9}), "<", ">", ","));    
    }

However, in response to the above problems, careful programmers have long discovered that our version of AndroidStudio3.0 gave us a good optimization tip, but there was no such tip before 3.0. As shown

 

AndroidStudio tool developer jetBrains actually integrates this hint directly into the Kotlin language they developed. They try to make fewer mistakes and detours at the grammatical level, and pay more attention to the implementation of the code itself; let you directly in the grammar At the level of, it is more clear and less confusing.

Second, how does Kotlin solve the pit of function calls

Kotlin can solve the problems encountered above. There is such a parameter in the Kotlin function called named parameter , which allows the function name to be specified in the function call, so that it can make the parameters and functions in the call place well. The defined parameters are one-to-one correspondence, and there is no problem of passing parameter confusion.

//kotlin一个函数的接口满足以上三种顺序调用的接口,准确来说是参数列表中任意参数顺序组合的调用
fun joinToString(nums: List<Int>, prex: String, sep: String, postfix: String): String {
    val builder = StringBuilder(prex)
    for (i in nums.indices) {
        builder.append(i)
        if (i < nums.size - 1) {
            builder.append(sep)
        }
    }
    builder.append(postfix)
    return builder.toString()
}
fun main(args: Array<String>) {
    //调用kotlin函数接口,满足张三接口设计需求,且调用更加明确
    println(joinToString(nums = listOf(1, 3, 5, 7, 9), prex = "<", sep = ",", postfix = ">"))
    //调用kotlin函数接口,满足李四接口设计需求,且调用更加明确
    println(joinToString(nums = listOf(1, 3, 5, 7, 9), sep = ",", prex = "<", postfix = ">"))
    //调用kotlin函数接口,满足王二接口设计需求,且调用更加明确
    println(joinToString(nums = listOf(1, 3, 5, 7, 9), prex = "<", postfix = ">", sep = ","))
}

AndroidStudio3.0 function call highlights with named parameters are more eye-catching

 

Summary: Through the above examples, it can be concluded that Kotlin is indeed clearer than Java in terms of function calls, and it also prevents us from stepping on some unnecessary pits. There is no harm without comparison. Compared to Java, do you think Kotlin is more suitable for you?

Three, what kind of pits does Java have in function overloading

Regardless of whether it is in Java or C++, there are function overloading. The purpose of function overloading is to target different functional business requirements, and then expose different parameter interfaces, including the number of parameter lists, parameter types, and parameter order. In other words, almost every different requirement needs a function to correspond to. With future expansion, functions with the same name in this class will pile up, and there are hierarchical calls between each function, and the parameters between functions The difference in the list is sometimes subtle, so the caller will feel very confused. The code hints find that there are seven or eight identical methods. For example (Android image loading framework we are used to encapsulate it again for easy calling)

//注意:这是我早期写出来kotlin代码(很丑陋),虽然这个看起来是kotlin代码,但是并没有脱离Java语言的思想
//束缚,也没有利用起kotlin的新特性,这个封装完全可以看做是直接从java代码翻译过来的kotlin代码。
fun ImageView.loadUrl(url: String) {//ImageView.loadUrl这个属于扩展函数,后期会介绍,暂时可以先忽略
	loadUrl(Glide.with(context), url)
}

fun ImageView.loadUrl(requestManager: RequestManager, url: String) {
	loadUrl(requestManager, url, false)
}

fun ImageView.loadUrl(requestManager: RequestManager, url: String, isCrossFade: Boolean) {
	ImageLoader.newTask(requestManager).view(this).url(url).crossFade(isCrossFade).start()
}

fun ImageView.loadUrl(urls: List<String>) {
	loadUrl(Glide.with(context), urls)
}

fun ImageView.loadUrl(requestManager: RequestManager, urls: List<String>) {
	loadUrl(requestManager, urls, false)
}

fun ImageView.loadUrl(requestManager: RequestManager, urls: List<String>, isCrossFade: Boolean) {
	ImageLoader.newTask(requestManager).view(this).url(urls).crossFade(isCrossFade).start()
}

fun ImageView.loadRoundUrl(url: String) {
	loadRoundUrl(Glide.with(context), url)
}

fun ImageView.loadRoundUrl(requestManager: RequestManager, url: String) {
	loadRoundUrl(requestManager, url, false)
}

fun ImageView.loadRoundUrl(requestManager: RequestManager, url: String, isCrossFade: Boolean) {
	ImageLoader.newTask(requestManager).view(this).url(url).crossFade(isCrossFade).round().start()
}

fun ImageView.loadRoundUrl(urls: List<String>) {
	loadRoundUrl(Glide.with(context), urls)
}

fun ImageView.loadRoundUrl(requestManager: RequestManager, urls: List<String>) {
	loadRoundUrl(requestManager, urls, false)
}

fun ImageView.loadRoundUrl(requestManager: RequestManager, urls: List<String>, isCrossFade: Boolean) {
	ImageLoader.newTask(requestManager).view(this).url(urls).crossFade(isCrossFade).round().start()
}
//调用的地方
activity.home_iv_top_banner.loadUrl(bannerUrl)
activity.home_iv_top_portrait.loadUrl(portraitUrls)
activity.home_iv_top_avatar.loadRoundUrl(avatarUrl)
activity.home_iv_top_avatar.loadRoundUrl(avatarUrls)

//以上的代码,相信很多人在Java中看到有很多吧,先不说个人写的,就拿官方库中的Thread类的构造器方法就有
//七八个。说明函数重载往往在符合需求接口扩展的时候,也在渐渐埋下了坑。不说别的就拿这个类来说,即使直
//接看函数定义,你也得花时间去理清里面的调用关系,然后才能放心去使用。而且这样函数以后维护起来特别麻烦。

Fourth, how does Kotlin solve the pitfalls caused by function overloading

For the overloaded method of the above example, in fact, it only needs one method to solve the implementation by handing to Kotlin, and it is very convenient to call. In fact, there is a function parameter called default value parameter in Kotlin . It can solve the problem of function overloading, and it will be very convenient and simple to use in conjunction with the named parameters we mentioned above at the place of call .

//学完命名参数和默认值参数函数,立即重构后的样子
fun ImageView.loadUrl(requestManager: RequestManager = Glide.with(context)
					  , url: String = ""
					  , urls: List<String> = listOf(url)
					  , isRound: Boolean = false
					  , isCrossFade: Boolean = false) {
	if (isRound) {
		ImageLoader.newTask(requestManager).view(this).url(urls).round().crossFade(isCrossFade).start()
	} else {
		ImageLoader.newTask(requestManager).view(this).url(urls).crossFade(isCrossFade).start()
	}
}
//调用的地方
activity.home_iv_top_banner.loadUrl(url = bannerUrl)
activity.home_iv_top_portrait.loadUrl(urls = portraitUrls)
activity.home_iv_top_avatar.loadUrl(url = avatarUrl, isRound = true)
activity.home_iv_top_avatar.loadUrl(urls = avatarUrls, isRound = true)

Summary: In Kotlin, when a function defined by Kotlin is called, the names of some parameters can be displayed and the order of parameter calling can be disturbed, because the specific corresponding parameters can be uniquely located by the parameter name. Through the above code, it is found that Kotlin's default value function perfectly solves the problem of function overloading, and the named function solves the problem of function calling, and realizes that the parameters of the function can be called by specifying the parameter name in any order.

5. What issues should be paid attention to when overloading functions when Java and Kotlin call each other

5.1 Use @JvmOverloads annotation to solve the problem of Java calling Kotlin overloaded functions

Since there is no concept of default value parameters in Java, when we need to call the default value overloaded function in Kotlin from Java, we must explicitly specify all parameter values. But this is definitely not what we want, otherwise Kotlin will lose the meaning of overloading and cannot fully interoperate with Java. So another solution in Kotlin is to use the @JvmOverloads annotation so that multiple overloaded methods will be automatically generated for Java to call. You can see the following example through the Kotlin code

@JvmOverloads
fun <T> joinString(
        collection: Collection<T> = listOf(),
        separator: String = ",",
        prefix: String = "",
        postfix: String = ""
): String {
    return collection.joinToString(separator, prefix, postfix)
}
//调用的地方
fun main(args: Array<String>) {
    //函数使用命名参数可以提高代码可读性
    println(joinString(collection = listOf(1, 2, 3, 4), separator = "%", prefix = "<", postfix = ">"))
    println(joinString(collection = listOf(1, 2, 3, 4), separator = "%", prefix = "<", postfix = ">"))
    println(joinString(collection = listOf(1, 2, 3, 4), prefix = "<", postfix = ">"))
    println(joinString(collection = listOf(1, 2, 3, 4), separator = "!", prefix = "<"))
    println(joinString(collection = listOf(1, 2, 3, 4), separator = "!", postfix = ">"))
    println(joinString(collection = listOf(1, 2, 3, 4), separator = "!"))
    println(joinString(collection = listOf(1, 2, 3, 4), prefix = "<"))
    println(joinString(collection = listOf(1, 2, 3, 4), postfix = ">"))
    println(joinString(collection = listOf(1, 2, 3, 4)))
}

The default value of the parameter in Kotlin is compiled into the called function, not the calling place, so you need to recompile this function after changing the default value. We can see from the following decompiled code that Kotlin compiled the default values ​​into the function.

  // $FF: synthetic method
  // $FF: bridge method
  @JvmOverloads
  @NotNull
  public static String joinString$default(Collection var0, String var1, String var2, String var3, int var4, Object var5) {
     if((var4 & 1) != 0) {
        var0 = (Collection)CollectionsKt.emptyList();//默认值空集合
     }

     if((var4 & 2) != 0) {
        var1 = ",";//默认值分隔符“,”
     }

     if((var4 & 4) != 0) {
        var2 = "";//默认前缀
     }

     if((var4 & 8) != 0) {
        var3 = "";//默认后缀
     }

     return joinString(var0, var1, var2, var3);
  }

  @JvmOverloads
  @NotNull
  public static final String joinString(@NotNull Collection collection, @NotNull String separator, @NotNull String prefix) {
     return joinString$default(collection, separator, prefix, (String)null, 8, (Object)null);
  }

  @JvmOverloads
  @NotNull
  public static final String joinString(@NotNull Collection collection, @NotNull String separator) {
     return joinString$default(collection, separator, (String)null, (String)null, 12, (Object)null);
  }

  @JvmOverloads
  @NotNull
  public static final String joinString(@NotNull Collection collection) {
     return joinString$default(collection, (String)null, (String)null, (String)null, 14, (Object)null);
  }

  @JvmOverloads
  @NotNull
  public static final String joinString() {
     return joinString$default((Collection)null, (String)null, (String)null, (String)null, 15, (Object)null);
  }

5.2 Can Kotlin call Java with named parameters and default value parameters?

Note: You cannot use named parameters in functions in Kotlin. Even though many constructor methods or common methods are overloaded in Java, you cannot use named parameters when calling Java methods in Kotlin, regardless of whether you are a function in JDK or an Android framework. The functions in are not allowed to use named parameters.

At this point, have we found that Kotlin really makes our function calls easier, clear, and try it quickly

Guess you like

Origin blog.csdn.net/az44yao/article/details/112917961