Kotlin 泛型
泛型是强大的特性,它允许我们定义可以使用不同数据类型访问的类、方法和属性,同时保持对编译时类型安全性的检查。
创建参数化类 –
泛型类型是在类型上参数化的类或方法。我们总是使用尖括号 ( ) 来指定程序中的类型参数。
泛型类定义如下:
class MyClass(text: T) {
var name = text
}
要创建此类的实例,我们需要提供类型参数:
val my : MyClass = Myclass("GeeksforGeeks")
如果参数可以从构造函数的参数中推断出来,则可以省略类型参数:
val my = MyClass("GeeksforGeeks")
在这里,GeeksforGeeks 的类型为 String,因此编译器发现我们正在谈论 Myclass
通用的优点——
- 类型转换是不可避免的——不需要对对象进行类型转换。
- 类型安全- 泛型一次只允许单一类型的对象。
- 编译时安全- 在编译时检查泛型代码的参数化类型,以避免运行时错误。
在我们的程序中通用-
在下面的示例中,我们使用具有单个参数的主构造函数创建了一个Company类。现在,我们尝试将 Company 类的对象中不同类型的数据作为 String 和 Integer 传递。 Company 类的主构造函数接受字符串类型( “GeeskforGeeks” ),但在传递整数类型( 12 )时会出现编译时错误。
没有泛型类的 Kotlin 程序-
class Company (text: String) {
var x = text
init{
println(x)
}
}
fun main(args: Array){
var name: Company = Company("GeeksforGeeks")
var rank: Company = Company(12)// compile time error
}
输出:
Error:(10, 33) Kotlin: The integer literal does not conform to the expected type String
为了解决上述问题,我们可以创建一个用户定义的泛型类型类,它在单个类中接受不同类型的参数。类公司类型是一个通用类型类,它接受 Int 和 String 类型的参数。
使用泛型类的 Kotlin 程序-
class Company (text : T){
var x = text
init{
println(x)
}
}
fun main(args: Array){
var name: Company = Company("GeeksforGeeks")
var rank: Company = Company(12)
}
输出:
GeeksforGeeks
1234
差异——
与Java不同,Kotlin 默认使数组保持不变。通过扩展,泛型类型在 Kotlin 中是不变的。这可以通过out和in关键字进行管理。不变性是已经为特定数据类型定义的标准泛型函数/类不能接受或返回另一种数据类型的属性。 Any是所有其他数据类型的超类型。
方差有两种类型——
- 声明站点差异(使用输入和输出)
- 使用地点差异:类型投影
Kotlin 出入关键字——
out
关键字——
在 Kotlin 中,我们可以在泛型类型上使用out
关键字,这意味着我们可以将此引用分配给它的任何超类型。 out
值只能由给定类产生,但不能消耗:
class OutClass(val value: T) {
fun get(): T {
return value
}
}
上面,我们定义了一个可以产生 T 类型值的OutClass
类。然后,我们可以将 OutClass 的一个实例分配给它的超类型引用:
val out = OutClass("string")
val ref: OutClass = out
注意:如果我们没有在上面的类中使用out
类型,那么给定的语句将产生编译错误。
in
关键字 –
如果我们想将它分配给它的子类型的引用,那么我们可以在泛型类型上使用in
关键字。 in
关键字只能用于使用的参数类型,而不是生成的参数类型:
class InClass {
fun toString(value: T): String {
return value.toString()
}
}
在这里,我们声明了一个 toString() 方法,它只使用 T 类型的值。然后,我们可以将 Number 类型的引用分配给它的子类型 Int 的引用:
val inClassObject: InClass = InClass()
val ref = inClassObject
注意:如果我们没有在上面的类中使用 in 类型,那么给定的语句将产生编译器错误。
协方差——
协方差意味着替换子类型是可以接受的,但超类型不是,即泛型函数/类可以接受它已经定义的数据类型的子类型,例如为 Number 定义的泛型类可以接受 Int,但为 Int 定义的泛型类不能接受数字。这可以在 Kotlin 中使用out
关键字实现,如下所示 -
fun main(args: Array) {
val x: MyClass = MyClass() // Error: Type mismatch
val y: MyClass = MyClass() // Works since String is a subtype of Any
val z: MyClass = MyClass() // Error since Any is a supertype of String
}
class MyClass
我们可以通过将 out 关键字附加到声明站点来直接允许协方差。下面的代码工作得很好。
fun main(args: Array) {
val y: MyClass = MyClass() // Compiles without error
}
class MyClass
协方差——
它用于替换子类型中的超类型值,即泛型函数/类可以接受已经为其定义的数据类型的超类型,例如为 Number 定义的泛型类不能接受 Int,但为 Int 定义的泛型类可以接受数字。它是在 Kotlin 中使用in关键字实现的,如下所示 -
fun main(args: Array) {
var a: Container = Container() //compiles without error
var b: Container = Container() //gives compilation error
}
open class Animal
class Dog : Animal()
class Container
类型预测 –
如果我们想将某个类型的数组的所有元素复制到 Any 类型的数组中,那么这是可能的,但是为了让编译器能够编译我们的代码,我们需要使用out
关键字注释输入参数。这使得编译器推断输入参数可以是 Any 的子类型的任何类型:
Kotlin 程序将一个数组的元素复制到另一个数组中——
fun copy(from: Array, to: Array) {
assert(from.size == to.size)
// copying (from) array to (to) array
for (i in from.indices)
to[i] = from[i]
// printing elements of array in which copied
for (i in to.indices) {
println(to[i])
}
}
fun main(args :Array) {
val ints: Array = arrayOf(1, 2, 3)
val any :Array = Array(3) { "" }
copy(ints, any)
}
输出:
1
2
3
星星投影——
当我们不知道值的具体类型并且只想打印数组的所有元素时,我们使用 star(*) 投影。
使用星形投影的 Kotlin 程序 –
// star projection in array
fun printArray(array: Array<*>) {
array.forEach { print(it) }
}
fun main(args :Array) {
val name = arrayOf("Geeks","for","Geeks")
printArray(name)
}
输出:
GeeksforGeeks
在评论中写代码?请使用 ide.geeksforgeeks.org,生成链接并在此处分享链接。