English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Kotlin基础教程

Kotlin流程控制

Функции в Kotlin

Строки в Kotlin

Kotlin面向对象(OOP)

Генерик в Kotlin

Универсальность, или "параметризированные типы", параметризация типов, которая может использоваться в классах, интерфейсах и методах.

Как и Java, Kotlin также предоставляет универсальность, обеспечивая безопасность типов и устраняя проблемы с типовой конверсией.

Декларация универсального класса:

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

При создании примера класса необходимо указать параметр типа:

val box: Box<Int> = Box<Int>(1)
// Или
val box = Box(1) // Компилятор выполняет типовую инференцию, 1 тип Int, поэтому компилятор знает, что мы говорим Box<Int>.

Ниже приведен пример传入 целого числа и строки в универсальный класс Box:

class Box<T>(t: T) {
    var value = t
}
fun main(args: Array<String>) {
    var boxInt = Box<Int>(10)
    var boxString = Box<String>("w3codebox")
    println(boxInt.value)
    println(boxString.value)
}

Результат вывода:

10
w3codebox

Определение универсального типа переменной можно полностью написать параметры типа, если компилятор может автоматически определить параметры типа, можно также пропустить параметры типа.

Декларация универсальной функции Kotlin аналогична Java, параметры типа должны быть放在 имени функции перед именем.

fun <T> boxIn(value: T) = Box(value)
// Ничто из следующего не является недопустимым выражением
val box4 = boxIn<Int>(1)
val box5 = boxIn(1) // Компилятор выполняет типовую инференцию

При вызове универсальной функции, если можно определить параметр типа, можно пропустить параметр типа.

Ниже приведен пример создания универсальной функции doPrintln, которая выполняет соответствующую обработку в зависимости от传入ного типа:

fun main(args: Array<String>) {
    val age = 23
    val name = "w3codebox"
    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 не является целым числом,也不是 строкой")
    }
}

Результат вывода:

Целое число 23
Преобразование строки в верхний регистр: w3codebox
T не является целым числом и не является строкой

Ограничения generics

Мы можем использовать ограничения generics для установления типов, которые могут использоваться для данного параметра.

В Kotlin для ограничения верхних границ типов generics используется :

Самым распространенным ограничением является верхняя граница (upper bound):

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

Подтипы Comparable могут заменить T. Например:

sort(listOf(1, 2, 3)) // Верно. Int является подтипом Comparable<Int>
sort(listOf(HashMap<Int, String>())) // Ошибка: HashMap<Int, String> не является подтипом Comparable<HashMap<Int, String>>

По умолчанию верхняя граница - Any?.

Для нескольких ограничений верхних границ можно использовать подзадачу 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() }
}

Ковариантность

В Kotlin нет универсальных типов, у него есть две другие вещи: ковариантность типа в месте объявления (declaration-site variance) и типовые проекции (type projections).

Ковариантность типа в месте объявления

Использование ковариантности типа в месте объявления с помощью модификатора ковариантности: in, out. Потребители in, производители out.

Использование out для ковариантности типа параметра, ковариантные типы параметров могут использоваться только в качестве типов возвращаемых значений, но не могут использоваться в качестве типов входных параметров:

// Определение класса, поддерживающего ковариантность
class w3codebox<out A>(val a: A) {
    fun foo(): A {
        return a
    }
}
fun main(args: Array<String>) {
    var strCo: w3codebox<String> = w3codebox("a")
    var anyCo: w3codebox<Any> = w3codebox<Any>("b")
    anyCo = strCo
    println(anyCo.foo()) // Вывод a
}

in делает типовой параметр контравariantным, контравariantные типовые параметры могут использоваться только в качестве входных, могут быть типами параметров входных данных, но не могут быть типами параметров возвращаемых значений:

// Определить класс, поддерживающий контравariance
class w3codebox<in A>(a: A) {
    fun foo(a: A) {
    }
}
fun main(args: Array<String>) {
    var strDCo = w3codebox("a")
    var anyDCo = w3codebox<Any>("b")
    strDCo = anyDCo
}

Проекция звездой

Иногда может потребоваться показать, что вы не знаете никакой информации о типовом параметре, но все же хотите безопасно использовать его. Здесь "безопасно использовать" означает определить типовую проекцию для генерического типа, требуя, чтобы все экземпляры этого типа были подтипами этой проекции.

Для этой проблемы Kotlin предоставляет синтаксис, называемый проекцией звездой (star-projection):

  • Если тип определен как Foo<out T>, где T - ковариантный типовой параметр, верхняя граница (upper bound) TUpper, Foo<> Эквивалентно Foo<out TUpper>. Это означает, что когда T неизвестен, вы можете безопасно читать из Foo<> Читать значение типа TUpper.

  • Если тип определен как Foo<in T>, где T - контравariantный типовой параметр, Foo<> Эквивалентно Foo<in Nothing>. Это означает, что когда T неизвестен, вы не можете безопасно записать в Foo<> Записать что-либо.

  • Если тип определен как Foo<T>, где T - ковариантный типовой параметр, верхняя граница (upper bound) TUpper, для случая чтения значений Foo<*> эквивалентен Foo<out TUpper>, для случая записи значений эквивалентен Foo<in Nothing>.

Если в генерической типе существует несколько типовых параметров, каждый параметр может быть использован отдельно для投射. Например, если тип определен как interface Function<in T, out U>, то могут использоваться следующие типы проекций звездой:

  • Function<*, String> , представляет собой Function<in Nothing, String> ;

  • Function<Int, *> , представляет собой Function<Int, out Any?> ;

  • Function<, > , представляет собой Function<in Nothing, out Any?> .

Внимание: Астерisks projection очень похожи на исходные типы Java, но могут быть использованы безопасно