Aprendizaje de Kotlin: genéricos de Kotlin

Genéricos de Kotlin

Los genéricos, a saber, "tipos parametrizados", parametrizan tipos y se pueden utilizar en clases, interfaces y métodos.

Al igual que Java, Kotlin también proporciona genéricos, lo que garantiza la seguridad de los tipos y elimina el problema de la conversión forzada.

Declare una clase genérica:

class Box<T>(t: T) {
    var value = t
}

Al crear una instancia de la clase, necesitamos especificar el parámetro de tipo

val box: Box<Int> = Box<Int>(1)
// 或者
val box = Box(1) // 编译器会进行类型推断,1 类型 Int,所以编译器知道我们说的是 Box<Int>。

El siguiente ejemplo pasa datos enteros y cadenas a la clase genérica Box

class Box<T>(t : T) {
    var value = t
}

fun main(args: Array<String>) {
    var boxInt = Box<Int>(10)
    var boxString = Box<String>("Runoob")

    println(boxInt.value)
    println(boxString.value)
}

El resultado de salida es

10
Runoob

Para definir una variable de tipo genérico, puede especificar completamente los parámetros de tipo. Si el compilador puede inferir automáticamente los parámetros de tipo, también puede omitir los parámetros de tipo.

La declaración de las funciones genéricas de Kotlin es la misma que la de Java, y los parámetros de tipo deben colocarse delante del nombre de la función.

fun <T> boxIn(value: T) = Box(value)

// 以下都是合法语句
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // 编译器会进行类型推断

Cuando se llama a una función genérica, si se puede inferir el parámetro de tipo, se puede omitir el parámetro genérico.

El siguiente ejemplo crea una función genérica doPrintln, y la función realiza el procesamiento correspondiente de acuerdo con los diferentes tipos pasados ​​en

fun main(args: Array<String>) {
    val age = 23
    val name = "runoob"
    val bool = true

    doPrintln(age)    // 整型
    doPrintln(name)   // 字符串
    doPrintln(bool)   // 布尔型
}

fun <T> doPrintln(content: T) {

    when (content) {
        is Int -> println("整型数字为 $content")
        is String -> println("字符串转换为大写:${content.toUpperCase()}")
        else -> println("T 不是整型,也不是字符串")
    }
}

El resultado de salida es

整型数字为 23
字符串转换为大写:RUNOOB
T 不是整型,也不是字符串

Restricciones genéricas

Podemos usar restricciones genéricas para establecer los tipos permitidos para un parámetro dado.

Usado en Kotlin: restringe el límite superior de tipos genéricos.

La restricción más común es el límite superior

fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
}

Los subtipos de Comparable pueden reemplazar a T. P.ej

sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子类型
sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型

El límite superior predeterminado es Cualquiera?

Para múltiples restricciones de límite superior, puede usar la cláusula where

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
    where T : CharSequence,
          T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}

Cambio de tipo

No hay un tipo de comodín en Kotlin, tiene otras dos cosas: la variación del sitio de declaración y las proyecciones de tipo

Declaración

La mutación de tipo en la declaración usa modificadores de anotación covariantes: entrada, salida, consumidor dentro, productor fuera.

Úselo para hacer un parámetro de tipo covariante. Los parámetros de tipo covariante solo se pueden usar como salida. Se pueden usar como tipos de valor de retorno, pero no como parámetros de entrada.

// 定义一个支持协变的类
class Runoob<out A>(val a: A) {
    fun foo(): A {
        return a
    }
}

fun main(args: Array<String>) {
    var strCo: Runoob<String> = Runoob("a")
    var anyCo: Runoob<Any> = Runoob<Any>("b")
    anyCo = strCo
    println(anyCo.foo())   // 输出 a
}

in hace que un parámetro de tipo sea contravariante, los parámetros de tipo contravariante solo se pueden usar como entrada, se pueden usar como el tipo de parámetro de entrada pero no como el tipo de valor de retorno

// 定义一个支持逆变的类
class Runoob<in A>(a: A) {
    fun foo(a: A) {
    }
}

fun main(args: Array<String>) {
    var strDCo = Runoob("a")
    var anyDCo = Runoob<Any>("b")
    strDCo = anyDCo
}

Proyección de estrellas

A veces, es posible que desee indicar que no conoce ninguna información sobre el parámetro de tipo, pero aún desea poder usarlo de manera segura. El llamado "uso seguro" aquí significa que una proyección de tipo se define para un tipo genérico , y el tipo genérico es obligatorio. Todas las instancias de entidad de son subtipos de esta proyección.

Para este problema, Kotlin proporciona una sintaxis llamada proyección de estrellas:

  • Si el tipo se define como Foo <out T>, donde T es un parámetro de tipo covariante, el límite superior es Tupper y Foo < > es equivalente a Foo <out TUpper>. Significa que cuando T es desconocido, es seguro leer un valor de tipo Tupper de Foo < >.
  • Si el tipo se define como Foo <en T>, donde T es un parámetro de tipo covariante inverso, Foo < > es equivalente a Foo <inNothing>. Significa que cuando T es desconocido, no se puede escribir con seguridad en Foo < > en nada .
  • Si el tipo se define como Foo <T>, donde T es un parámetro de tipo covariante, y el límite superior es Tupper, para leer el valor, Foo <*> es equivalente a Foo <out TUpper>, para escribir Al ingresar un valor , es equivalente a Foo <in Nothing>.

Si hay varios parámetros de tipo en un tipo genérico, entonces cada parámetro de tipo se puede proyectar por separado. Por ejemplo, si el tipo se define como función de interfaz <en T, fuera de U>, entonces pueden aparecer los siguientes tipos de proyecciones de asterisco:

  1. Función <*, Cadena>, 代表 Función <en Nada, Cadena>;
  2. Función <Int, *>, 代表 Función <Int, out Any?>;
  3. Función < >, 代表 Función <en Nada, fuera Cualquiera?>.

Nota: la proyección de asterisco es muy similar al tipo sin formato de Java, pero se puede utilizar de forma segura

Supongo que te gusta

Origin blog.csdn.net/PrisonJoker/article/details/113926226
Recomendado
Clasificación