YouTip LogoYouTip

Kotlin Generics

Generics, also known as "parameterized types", parameterize types and can be used on classes, interfaces, and methods. Like Java, Kotlin also provides generics to provide type safety guarantees and eliminate the hassle of type casting. Declare a generic class: class Box(t: T) { var value = t } When creating an instance of a class, we need to specify the type parameter: val box: Box = Box(1)// Or val box = Box(1) // The compiler will perform type inference,1 The type is Int, so the compiler knows we are referring to Box. The following example passes integer data and strings to the generic class Box: class Box(t : T) { var value = t } fun main(args: Array) { var boxInt = Box(10) var boxString = Box("Tutorial") println(boxInt.value) println(boxString.value)} The output is: 10Tutorial When defining generic type variables, you can fully specify the type parameters. If the compiler can automatically infer the type parameters, you can also omit the type parameters. The declaration of generic functions in Kotlin is the same as in Java, with type parameters placed before the function name: fun boxIn(value: T) = Box(value)// The following are all valid statements: val box4 = boxIn(1) val box5 = boxIn(1) // The compiler will perform type inference When calling a generic function, if the type parameter can be inferred, you can omit the generic parameter. The following example creates a generic function doPrintln, which handles different types accordingly: fun main(args: Array) { val age = 23 val name = "tutorial" val bool = true doPrintln(age) // Integer doPrintln(name) // String doPrintln(bool) // Boolean} fun doPrintln(content: T) { when (content) { is Int -> println("Integer number is $content") is String -> println("String converted to uppercase:${content.toUpperCase()}") else -> println("T It is neither an integer nor a string.") }} The output is: Integer number is 23String converted to uppercase:TUTORIAL T It is neither an integer nor a string. * * * ## Generic Constraints We can use generic constraints to set the allowed types for a given parameter. In Kotlin, use : to constrain the upper bound of a generic type. The most common constraint is the upper bound: fun <T : Comparable> sort(list: List) { // ……} Subtypes of Comparable can substitute T. For example: sort(listOf(1, 2, 3)) // OK。Int is Comparable subtype of sort(listOf(HashMap())) // Error: HashMap is not Comparable<HashMap> subtype of The default upper bound is Any?. For multiple upper bound constraints, use a where clause: fun copyWhenGreater(list: List, threshold: T): List where T : CharSequence, T : Comparable { return list.filter { it > threshold }.map { it.toString() }} * * * ## Variance Kotlin does not have wildcard types. It has two other things: declaration-site variance and type projections. ### Declaration-site variance Type variance at the declaration site uses covariant annotation modifiers: in, out. Consumer in, producer out. Using out makes a type parameter covariant. Covariant type parameters can only be used as output, can be used as return types but cannot be used as parameter types: // Define a covariant class class Tutorial(val a: A) { fun foo(): A { return a }} fun main(args: Array) { var strCo: Tutorial = Tutorial("a") var anyCo: Tutorial = Tutorial("b") anyCo = strCo println(anyCo.foo()) // Output a} Using in makes a type parameter contravariant. Contravariant type parameters can only be used as input, can be used as parameter types but cannot be used as return types: // Define a contravariant class class Tutorial(a: A) { fun foo(a: A) { }} fun main(args: Array) { var strDCo = Tutorial("a") var anyDCo = Tutorial("b") strDCo = anyDCo } * * * ## Star Projection Sometimes, you may want to indicate that you don't know any information about the type parameter, but still want to be able to use it safely. Here "safely use" means defining a type projection for the generic type, requiring that all entity instances of this generic type are subtypes of this projection. For this problem, Kotlin provides a syntax called star-projection: * If the type is defined as Foo, where T is a covariant type parameter with upper bound TUpper, Foo is equivalent to Foo. It means when T is unknown, you can safely read values of type TUpper from Foo. * If the type is defined as Foo, where T is a contravariant type parameter, Foo is equivalent to Foo. It means when T is unknown, you cannot safely write anything to Foo. * If the type is defined as Foo, where T is a covariant type parameter with upper bound TUpper, for reading values, Foo is equivalent to Foo, for writing values, it is equivalent to Foo. If a generic type has multiple type parameters, each type parameter can be projected individually. For example, if the type is defined as interface Function, then the following star projections can appear: 1. Function, represents Function; 2. Function, represents Function; 3. Function, represents Function. Note: Star projection is very similar to Java
← Python3 Att Dictionary PopKotlin Extend β†’