Kotlin 中具有两个类型参数的泛型 class
Generics class in Kotlin with two type parameters
class MapBuilder<T,U> {
operator fun invoke(arg: T): MapBuilder<T, U> {
return this
}
operator fun invoke(arg: U): MapBuilder<T, U> {
return this
}
}
当然,由于 JVM 的限制,它无法正常工作。
Platform declaration clash: The following declarations have the same JVM signature (invoke(Ljava/lang/Object;)Lcom/test/tests/MapBuilder;):
operator fun invoke(arg: T): MapBuilder<T, U> defined in com.test.tests.MapBuilder
operator fun invoke(arg: U): MapBuilder<T, U> defined in com.test.tests.MapBuilder
有什么想法吗?我该如何实现?
这是因为重载冲突。
实际上,使用您当前的参数,T
可以等于 U
。如果您熟悉重载,您应该知道这是不允许的:
fun something(x: Int){ /* foo */ }
fun something(x: Int){ /* bar */ }
但举个例子:
fun something(x: Int){ /* foo */ }
fun something(x: Float){ /* bar */ }
因为它们可能相同,所以会导致冲突。它怎么知道要调用哪个方法?
对于完整范围,编译器会报错。如果你在一个参数上使用 : SomeClass
,它将停止抱怨。但这是一个随机示例:
class MapBuilder<T, U : Logger> {
operator fun invoke(arg: T): MapBuilder<T, U> {
return this
}
operator fun invoke(arg: U): MapBuilder<T, U> {
return this
}
}
fun t(){
MapBuilder<Logger, Logger>().invoke(LoggerFactory.getLogger(""))
}
invoke
会产生歧义。现在只有当你有两个相同的类型时才会出现这个问题;它使用哪个?
现在,您的 MCVE 非常少。我不知道你用 T 和 U 做什么。因此,我无法为您提供任何代码示例。但这是你需要知道的:
您不能使用任何类型的两种方法,因为它们可能会发生冲突。如果您使用两个相同的类型,即使使用方差也会导致重载问题。所以它会排除 MapBuilder<Int, Int>
作为一个实例。
您可以使用一个方法,也可以将它们拆分为两个不同名称的方法。这个名字表明它是一个生成器,所以你可以有 withKey(T t)
和 withValue(U u)
没有办法直接禁止 T == U,而不传递 Class<T>
和 Class<U>
并检查它们。不幸的是,编译器不理解这一点,即使使用 require
或其他合约函数也是如此。此外,在您尝试之前,使用 : Any
不起作用。那是默认的界限。请记住,在 Java 中,一切都是 Object
,在 Kotlin 中,一切都是 Any
。
您可以使用@JvmName(在's answer), but you'd use two different method names if you interop with Java. It might be slightly easier if you only use Kotlin though. Java-Kotlin interop has a bunch of @Jvm*
annotations, most/all of which are covered in the docs中提到)解决这个问题。
即使使用@JvmName,它仍然会允许 <String, String>
,直到调用冲突的方法。如果你想断言 T != U 无论如何,你需要 运行 class 检查。
在给定未知泛型类型的情况下,这些方法可以有效地具有相同的签名。因此,所呈现的基本情况对于 JVM 而言是不明确的。所以你只需要给它们一个替代名称,JVM(和 Java 或其他 JVM 语言)可以从中查看它们。您可以在 一个 或两者上使用 @JvmName
注释来为它们指定内部名称。这根本不会影响 Kotlin 和您在 Kotlin 代码中使用的名称,它们将按原样显示。
class MapBuilder<T,U> {
@JvmName("invokeWithT")
operator fun invoke(arg: T): MapBuilder<T, U> {
return this
}
@JvmName("InvokeWithU") // technically don't need both of these
operator fun invoke(arg: U): MapBuilder<T, U> {
return this
}
}
现在你很好,可以独立使用它们了。
val builder = MapBuilder<String, Integer>()
builder("hi") // success!
builder(123) // success!
请注意,如果 T
和 U
不明确,您在尝试调用它们时可能会遇到其他错误。
val builder = MapBuilder<String, String>()
builder("hi") // error!
Error:(y, x) Kotlin: Overload resolution ambiguity:
@JvmName public final operator fun invoke(arg: String): MapBuilder defined in MapBuilder
@JvmName public final operator fun invoke(arg: String): MapBuilder defined in MapBuilder
如果您可以以一种它们可能不会重叠并且相同的方式定义您的泛型,您也可以解决这个问题 class。您 可能 根据所选的实际通用参数得到错误,但至少您的基本声明是允许的。这在 .
中有更详细的描述
class MapBuilder<T,U> {
operator fun invoke(arg: T): MapBuilder<T, U> {
return this
}
operator fun invoke(arg: U): MapBuilder<T, U> {
return this
}
}
当然,由于 JVM 的限制,它无法正常工作。
Platform declaration clash: The following declarations have the same JVM signature (invoke(Ljava/lang/Object;)Lcom/test/tests/MapBuilder;):
operator fun invoke(arg: T): MapBuilder<T, U> defined in com.test.tests.MapBuilder
operator fun invoke(arg: U): MapBuilder<T, U> defined in com.test.tests.MapBuilder
有什么想法吗?我该如何实现?
这是因为重载冲突。
实际上,使用您当前的参数,T
可以等于 U
。如果您熟悉重载,您应该知道这是不允许的:
fun something(x: Int){ /* foo */ }
fun something(x: Int){ /* bar */ }
但举个例子:
fun something(x: Int){ /* foo */ }
fun something(x: Float){ /* bar */ }
因为它们可能相同,所以会导致冲突。它怎么知道要调用哪个方法?
对于完整范围,编译器会报错。如果你在一个参数上使用 : SomeClass
,它将停止抱怨。但这是一个随机示例:
class MapBuilder<T, U : Logger> {
operator fun invoke(arg: T): MapBuilder<T, U> {
return this
}
operator fun invoke(arg: U): MapBuilder<T, U> {
return this
}
}
fun t(){
MapBuilder<Logger, Logger>().invoke(LoggerFactory.getLogger(""))
}
invoke
会产生歧义。现在只有当你有两个相同的类型时才会出现这个问题;它使用哪个?
现在,您的 MCVE 非常少。我不知道你用 T 和 U 做什么。因此,我无法为您提供任何代码示例。但这是你需要知道的:
您不能使用任何类型的两种方法,因为它们可能会发生冲突。如果您使用两个相同的类型,即使使用方差也会导致重载问题。所以它会排除 MapBuilder<Int, Int>
作为一个实例。
您可以使用一个方法,也可以将它们拆分为两个不同名称的方法。这个名字表明它是一个生成器,所以你可以有 withKey(T t)
和 withValue(U u)
没有办法直接禁止 T == U,而不传递 Class<T>
和 Class<U>
并检查它们。不幸的是,编译器不理解这一点,即使使用 require
或其他合约函数也是如此。此外,在您尝试之前,使用 : Any
不起作用。那是默认的界限。请记住,在 Java 中,一切都是 Object
,在 Kotlin 中,一切都是 Any
。
您可以使用@JvmName(在@Jvm*
annotations, most/all of which are covered in the docs中提到)解决这个问题。
即使使用@JvmName,它仍然会允许 <String, String>
,直到调用冲突的方法。如果你想断言 T != U 无论如何,你需要 运行 class 检查。
在给定未知泛型类型的情况下,这些方法可以有效地具有相同的签名。因此,所呈现的基本情况对于 JVM 而言是不明确的。所以你只需要给它们一个替代名称,JVM(和 Java 或其他 JVM 语言)可以从中查看它们。您可以在 一个 或两者上使用 @JvmName
注释来为它们指定内部名称。这根本不会影响 Kotlin 和您在 Kotlin 代码中使用的名称,它们将按原样显示。
class MapBuilder<T,U> {
@JvmName("invokeWithT")
operator fun invoke(arg: T): MapBuilder<T, U> {
return this
}
@JvmName("InvokeWithU") // technically don't need both of these
operator fun invoke(arg: U): MapBuilder<T, U> {
return this
}
}
现在你很好,可以独立使用它们了。
val builder = MapBuilder<String, Integer>()
builder("hi") // success!
builder(123) // success!
请注意,如果 T
和 U
不明确,您在尝试调用它们时可能会遇到其他错误。
val builder = MapBuilder<String, String>()
builder("hi") // error!
Error:(y, x) Kotlin: Overload resolution ambiguity:
@JvmName public final operator fun invoke(arg: String): MapBuilder defined in MapBuilder
@JvmName public final operator fun invoke(arg: String): MapBuilder defined in MapBuilder
如果您可以以一种它们可能不会重叠并且相同的方式定义您的泛型,您也可以解决这个问题 class。您 可能 根据所选的实际通用参数得到错误,但至少您的基本声明是允许的。这在