Eliminate function overloading in Kotlin series

Tags: kotlin eliminate function overloading


content:


1. Problems caused by function overloading

  • 1. encountered problems

    Whether it is in Java or C++, there is function overloading. The purpose of function overloading is to meet different functional business requirements, and then expose the interface of different parameters, including the number of parameter lists, parameter types, and parameter order. However, the different adjustment of these parameters will undoubtedly add more functions with the same name, which is especially confusing for external callers. It is easy to call errors, especially when there are the same number of parameters but different parameter types, it is easy to make mistakes.

    Take a look at the following example (using the Thread class as an example):

    public class Thread{
    
     public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
     }
    
     public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
     }
    
     public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
     }
    
     public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
     }
    
    
    }
    class ThreadTest{
       public static void main(String[] args){
          Thread thread1 = new Thread();
    
          Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {//todo
    
            }
        });
    
          Thread thread3 = new Thread(null, new Runnable() {
            @Override
            public void run() {//todo
    
            }
        });
    
           Thread thread4 = new Thread(null, new Runnable() {
            @Override
            public void run() {//todo
    
            }
        }, "thread_test")  
       }
    }
    
  • 2. Solve the problem

    From the above problems, we know that there will be many overloaded methods in Java. If the call is wrong, maybe we will attribute this problem to not being rigorous enough, but who would have thought that if a good language is able to syntactically It is clear and reduces some grammatical errors. However, Kotlin is a language that is superior to Java in terms of its syntax, which reduces the possibility of code errors.
    The solution to the above problems is to use the function named parameters and default value parameters in Kotlin to completely eliminate the problem of function overloading.

    Kotlin solution example:

 class Thread(group: ThreadGroup? = null,
                   target: Runnable? = null,
                       name: String? = null)
  : Runnable{
      init(group, target, name, 0)
 }
 fun main(agrs: Array<String>){
    val thread1= Thread()
    val thread2 = Thread(target = object: Runnable{
          override fun run() {
                //todo
        }
      })
    val thread3 = Thread(target = object: Runnable{
          override fun run() {
                //todo
        }
      }, name = "therad_test")  
 }

2. Function named parameters in Kotlin

The first issue we need to pay attention to is the readability of functions. However, the appearance of function named parameters is to solve the readability of functions, to be precise, to make the caller more readable.

  • definition

    In Kotlin, when a function defined by Kotlin is called, the names of some parameters can be clearly marked, and the order of parameter calling can be disrupted, because the specific corresponding parameters can be uniquely located through the parameter names.

  • Example of calling in Java

    //定义一个Java函数
    public static String joinToString(Collection<T> collection, String prefix, String seperataor, String postfix){
    //...
    }
    
    //调用
    joinToString(collection, " ", " "," ")

    Calling the joinToString method from the above Java is actually quite confusing, especially the last three spaces, but it is impossible to know which parameter corresponds to the joinToString method from the place of the call, and what each value specifically means, then it is worth you to try. kotlin too.

  • Example of calling in Kotlin

    //函数使用默认值参数 可以消除重载
    @JvmOverloads
    fun <T> joinString(
        collection: Collection<T>,
        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 = ">"))
    }
    
    

    In fact, naming parameters alone cannot solve function overloading. Named functions are purely for the purpose of making the function call more explicit and the values ​​passed in by the caller can correspond to the parameters of the declared function. To solve the problem of function overloading, you must rely on default value parameters to solve

3. Function default value parameters in Kotlin

Named parameters are very useful when we are dealing with default value parameters, and generally use a combination of the two to solve the problem of function overloading. In Kotlin, function overloading can be avoided by specifying default values ​​for parameters when declaring a function. If you combine named parameters, you can omit some of the parameters in the middle, or you can give only the parameters you need in any order you want.

  //函数使用默认值参数 可以消除重载
@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), 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)))
}

operation result:

<1%2%3%4>
<1,2,3,4>
<1!2!3!4]
[1!2!3!4>
[1!2!3!4]
<1,2,3,4]
[1,2,3,4>
[1,2,3,4]

Process finished with exit code 0

Fourth, the @JvmOverloads annotation solves 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 is given in Kotlin, which is to use the @JvmOverloads annotation to automatically generate multiple overloaded methods for Java to call. I can see the following example through Kotlin code

  • kotlin code:

    @JvmOverloads
    fun <T> joinString(
        collection: Collection<T> = listOf(),
        separator: String = ",",
        prefix: String = "",
        postfix: String = ""
    ): String {
    return collection.joinToString(separator, prefix, postfix)
    }
  • Decompile the code into Java code:

    // $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. Issues that need attention

  • 1. One thing to note when using named parameters for functions in Kotlin is that even if many constructor methods or ordinary methods are overloaded in Java, you cannot use named parameters when calling methods in Java in Kotlin, no matter if you are in JDK Functions or functions in the Android framework are not allowed to use named parameters.

  • 2. In Kotlin, the default value of the parameter is compiled into the called function, not the place where it is called, so the function needs to be recompiled after changing the default value. We can see from the following decompiled code that Kotlin compiles the default value into the function.

    @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);
    }

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325380017&siteId=291194637