Hablar sobre cómo mejorar las funciones en los artículos de gramática de Kotlin (3)

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

Breve descripción: Hoy es la tercera parte de la serie de charlas de Kotlin. Hablemos de las llamadas a funciones y la sobrecarga de funciones. Vea el título y sabrá que Kotlin tiene ventajas sobre Java en las llamadas a funciones. Primero tira los siguientes pozos (se estima que lo habrás pisado antes ...) y mira cómo llenamos los pozos paso a paso, desde los que experimentarás el encanto del lenguaje de Kotlin.

  • 1. ¿Qué tipo de problemas tiene Java en las llamadas a funciones?
  • 2. ¿Cómo resuelve Kotlin los errores de las llamadas a funciones?
  • 3. ¿Qué errores tiene Java en la sobrecarga de funciones?
  • 4. ¿Cómo resuelve Kotlin los problemas causados ​​por la sobrecarga de funciones?
  • 5. ¿A qué problemas se debe prestar atención al sobrecargar funciones cuando Java y Kotlin se llaman entre sí?

1. Problemas con las llamadas a funciones de Java

Tratemos de recordar que en el proceso de desarrollo de programas en Java, a menudo llamamos a algunos métodos. Algunas personas diseñan métodos con muchos parámetros y, a veces, el nombre de los parámetros no está estandarizado (no puede cumplir con el nombre y el conocimiento), y el diseño Varios de los mismos tipos de parámetros todavía están uno al lado del otro. Esto en realidad trae muchos problemas y molestias a la persona que llama. Un programador cauteloso localizará el lugar donde esta función está definida y probablemente observará el orden de las llamadas de parámetros y la función de cada parámetro. Especialmente cuando este método está empaquetado en una biblioteca, es problemático verificarlo. Y el mismo tipo de parámetros uno al lado del otro es fácil de corresponder al incorrecto. Echemos un vistazo a este ejemplo (ahora el requisito es: imprimir los elementos en cada conjunto de acuerdo con el prefijo, separador y sufijo)

    //张三设计的函数接口顺序(调用者必须按照这个顺序传)
    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}), "<", ">", ","));    
    }

Sin embargo, en respuesta a los problemas anteriores, los programadores cuidadosos han descubierto durante mucho tiempo que nuestra versión de AndroidStudio3.0 nos dio una buena sugerencia de optimización, pero no existía tal sugerencia antes de la 3.0. Como se muestra

 

El desarrollador de herramientas de AndroidStudio, jetBrains, integra esta sugerencia directamente en el lenguaje Kotlin que desarrollaron. Intentan cometer menos errores y desvíos en el nivel gramatical, y prestan más atención a la implementación del código en sí; te permiten acceder directamente a la gramática. de, es más claro y menos confuso.

2. ¿Cómo resuelve Kotlin los errores de las llamadas a funciones?

Kotlin puede resolver los problemas encontrados anteriormente. Existe un parámetro de este tipo en la función de Kotlin llamado parámetro con nombre , que permite que el nombre de la función se especifique en la llamada a la función, de modo que pueda hacer bien los parámetros y funciones en el lugar de la llamada. los parámetros definidos son correspondencia uno a uno, y no hay problema de confusión de parámetros de paso.

//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 = ","))
}

Los aspectos destacados de la llamada a la función AndroidStudio3.0 con parámetros con nombre son más llamativos

 

Resumen: a través de los ejemplos anteriores, se puede concluir que Kotlin es de hecho más claro que Java en términos de llamadas a funciones, y también nos impide pisar algunos pozos innecesarios. No hay daño sin comparación. Comparado con Java, ¿crees que ¿Kotlin es más adecuado para ti?

Tres, ¿qué tipo de pozos tiene Java en la sobrecarga de funciones?

Independientemente de si está en Java o C ++, hay sobrecarga de funciones. El propósito de la sobrecarga de funciones es apuntar a diferentes requisitos funcionales del negocio y luego exponer diferentes interfaces de parámetros, incluido el número de listas de parámetros, tipos de parámetros y orden de parámetros. Es decir, casi todos los requisitos diferentes necesitan una función a la que corresponder. Con la expansión futura, las funciones con el mismo nombre en esta clase se acumularán, y habrá llamadas jerárquicas entre cada función y los parámetros entre funciones La diferencia en el La lista es a veces sutil, por lo que la persona que llama se sentirá muy confundida.Las sugerencias de código encuentran que hay siete u ocho métodos idénticos. Por ejemplo (marco de carga de imágenes de Android, estamos acostumbrados a encapsularlo nuevamente para facilitar las llamadas)

//注意:这是我早期写出来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类的构造器方法就有
//七八个。说明函数重载往往在符合需求接口扩展的时候,也在渐渐埋下了坑。不说别的就拿这个类来说,即使直
//接看函数定义,你也得花时间去理清里面的调用关系,然后才能放心去使用。而且这样函数以后维护起来特别麻烦。

En cuarto lugar, ¿cómo resuelve Kotlin los problemas causados ​​por la sobrecarga de funciones?

Para el método sobrecargado del ejemplo anterior, de hecho, solo necesita un método para resolver la implementación entregándolo a Kotlin, y es muy conveniente llamar. De hecho, hay un parámetro de función llamado parámetro de valor predeterminado en Kotlin . Puede resolver el problema de la sobrecarga de funciones y será muy conveniente y simple de usar junto con los parámetros nombrados que mencionamos anteriormente en el lugar de la llamada .

//学完命名参数和默认值参数函数,立即重构后的样子
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)

Resumen: En Kotlin, cuando se llama a una función definida por Kotlin, se pueden mostrar los nombres de algunos parámetros y se puede alterar el orden de llamada de los parámetros, porque los parámetros correspondientes específicos pueden ubicarse de forma única por el nombre del parámetro. A través del código anterior, se encuentra que la función de valor predeterminado de Kotlin resuelve perfectamente el problema de la sobrecarga de funciones, y la función nombrada resuelve el problema de la llamada a la función, y se da cuenta de que los parámetros de la función se pueden llamar especificando el nombre del parámetro en cualquier orden.

5. ¿A qué problemas se debe prestar atención al sobrecargar funciones cuando Java y Kotlin se llaman entre sí?

5.1 Utilice la anotación @JvmOverloads para resolver el problema de Java llamando a las funciones sobrecargadas de Kotlin

Dado que no existe un concepto de parámetros de valor predeterminado en Java, cuando necesitamos llamar a la función sobrecargada de valor predeterminado en Kotlin desde Java, debemos especificar explícitamente todos los valores de los parámetros. Pero esto definitivamente no es lo que queremos, de lo contrario, Kotlin perderá el significado de sobrecarga y no podrá interoperar completamente con Java. Entonces, otra solución en Kotlin es usar la anotación @JvmOverloads para que se generen automáticamente múltiples métodos sobrecargados para que Java los llame. Puedes ver el siguiente ejemplo a través del código de Kotlin

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

El valor predeterminado del parámetro en Kotlin se compila en la función llamada, no en el lugar de la llamada, por lo que debe volver a compilar esta función después de cambiar el valor predeterminado. Podemos ver en el siguiente código descompilado que Kotlin compiló los valores predeterminados en la función.

  // $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 ¿Puede Kotlin llamar a Java con parámetros con nombre y parámetros de valor predeterminado?

Nota: No puede usar parámetros con nombre en funciones en Kotlin. Aunque muchos métodos constructores o métodos comunes están sobrecargados en Java, no puede usar parámetros con nombre al llamar a métodos Java en Kotlin, independientemente de si es una función en JDK o en un marco de Android. Las funciones en no pueden utilizar parámetros con nombre.

En este punto, ¿hemos descubierto que Kotlin realmente hace que nuestras llamadas a funciones sean más fáciles, claras y pruébelo rápidamente?

Supongo que te gusta

Origin blog.csdn.net/az44yao/article/details/112917961
Recomendado
Clasificación