在java中,泛型上界可以使用
class ClassA<T extends InterfaceA & InterfaceB>{}
在kotlin中如何使用
用相似的语法来定义泛型的上界。在泛型类型参数后面使用where
关键字,并列出要实现的接口,就像在Java中使用&
符号一样。
下面是用Kotlin定义一个具有多个接口上界的泛型类的示例:
interface InterfaceA
interface InterfaceB
class ClassA<T> where T : InterfaceA, T : InterfaceB
在这个例子中,ClassA
是一个泛型类,它的类型参数T
必须同时实现InterfaceA
和InterfaceB
接口。
你还可以将类型参数的上界限制放在类声明中,就像在Java中一样:
interface InterfaceA
interface InterfaceB
class ClassA<T>(val value: T) where T : InterfaceA, T : InterfaceB
这个例子中,ClassA
是一个泛型类,它接受一个类型参数T
作为构造函数的参数。类型参数T
的上界限制是同时实现InterfaceA
和InterfaceB
接口。
当 Kotlin 的类使用了声明处型变,有两种选择可以从 Java 代码中看到它们的用法。让我们假设我们有以下类和两个使用它的函数:
class Box<out T>(val value: T)
interface Base
class Derived : Base
fun boxDerived(value: Derived): Box<Derived> = Box(value)
fun unboxBase(box: Box<Base>): Base = box.value
一种看似理所当然地将这俩函数转换成 Java 代码的方式可能会是:
Box<Derived> boxDerived(Derived value) { …… }
Base unboxBase(Box<Base> box) { …… }
问题是,在 Kotlin 中我们可以这样写 unboxBase(boxDerived(“s”)),但是在 Java 中是行不通的,因为在 Java 中类 Box 在其泛型参数 T 上是不型变的,于是 Box 并不是 Box 的子类。 要使其在 Java 中工作,我们按以下这样定义 unboxBase:
Base unboxBase(Box<? extends Base> box) { …… }
这里我们使用 Java 的通配符类型(? extends Base)来通过使用处型变来模拟声明处型变,因为在 Java 中只能这样。
当它作为参数出现时,为了让 Kotlin 的 API 在 Java 中工作,对于协变定义的 Box 我们生成 Box 作为 Box<? extends Super> (或者对于逆变定义的 Foo 生成 Foo<? super Bar>)。当它是一个返回值时, 我们不生成通配符,因为否则 Java 客户端将必须处理它们(并且它违反常用 Java 编码风格)。因此,我们的示例中的对应函数实际上翻译如下:
// 作为返回类型——没有通配符
Box<Derived> boxDerived(Derived value) { …… }
// 作为参数——有通配符
Base unboxBase(Box<? extends Base> box) { …… }
注意:当参数类型是 final 时,生成通配符通常没有意义,所以无论在什么地方 Box 始终转换为 Box。
如果我们在默认不生成通配符的地方需要通配符,我们可以使用 @JvmWildcard 注解:
fun boxDerived(value: Derived): Box<@JvmWildcard Derived> = Box(value)
// 将被转换成
// Box<? extends Derived> boxDerived(Derived value) { …… }
另一方面,如果我们根本不需要默认的通配符转换,我们可以使用**@JvmSuppressWildcards**
fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
// 会翻译成
// Base unboxBase(Box<Base> box) { …… }
注意:@JvmSuppressWildcards 不仅可用于单个类型参数,还可用于整个声明(如函数或类),从而抑制其中的所有通配符。
我可以使用where
关键字来实现联合泛型上界,并限定泛型类型必须是两个已知类型的子类。具体步骤如下:
where
关键字声明泛型类型的上界为T
和U
的子类。fun <T, U> combine(first: T, second: U) where T : ParentClass1, U : ParentClass2 {
// 方法体
}
first
和second
对象的公共属性或方法。由于泛型类型的上界限定了类型必须为T
和U
的子类,因此可以安全地使用这些属性和方法。fun <T, U> combine(first: T, second: U) where T : ParentClass1, U : ParentClass2 {
// 使用first和second的公共属性或方法
val result = "${first.property1}${second.property2}"
}
combine
方法时,传入符合要求的泛型类型实例。val obj1: ChildClass1 = ChildClass1()
val obj2: ChildClass2 = ChildClass2()
combine(obj1, obj2)
完整代码示例:
open class ParentClass1 {
val property1: String = "parent1"
}
open class ParentClass2 {
val property2: String = "parent2"
}
class ChildClass1 : ParentClass1()
class ChildClass2 : ParentClass2()
fun <T, U> combine(first: T, second: U) where T : ParentClass1, U : ParentClass2 {
val result = "${first.property1}${second.property2}"
}
fun main() {
val obj1: ChildClass1 = ChildClass1()
val obj2: ChildClass2 = ChildClass2()
combine(obj1, obj2)
}