Kotlin:覆盖(并指定)通用接口函数
Kotlin: Override (and specify) generic interface functions
我想使用将由其实现指定的通用接口 类,总而言之,这一切都运行良好,例如:
interface Remove <E> { fun remove(entity: E) }
class MyHandler : Remove <MyClass> {
override fun remove(entity: MyClass) { */do stuff/* }
}
但是,我有一个案例(到目前为止,期待更多)我希望函数本身是通用的。写接口完全没问题:
interface FindByID <E> { fun <K : Serializable> findByID(id: K): E }
我需要 K 是可序列化的,因为这是我需要调用的某些函数的要求。
编译器似乎不同意我的实现尝试。当我这样做时:
override fun <String> findByID(id: String): User {
return someFunction(User::class.java, id) as User
}
我遇到两个编译器错误:
- 什么都不覆盖
id
不可序列化
然而,当我从签名中删除 override
和 <String>
时,它工作正常。这意味着 String
是可序列化的,我的研究也表明了这一点。
这里似乎有什么问题?
此外,是的,我知道我可以通过多种方式解决这个问题,例如
- 使接口函数接受
Serializable
而不是 <K : Serializable>
- 未在覆盖时指定
K
,但在调用时指定 (myHandler.findByID<String>("myID")
)
- “重新路由”调用,在 MyHandler 中实现(而不是覆盖)一个接受字符串的函数,然后在内部调用被覆盖的函数
虽然愿意接受建议,但我对解决方法不太感兴趣,但更愿意了解并(如果可能)解决实际问题,或者至少知道它无法完成,所以我可以考虑到这一点用于规划
当前问题
根据您当前的 <String>
声明,您并没有像您想象的那样专门化类型参数。您实际上在做的是声明一个恰好命名为 String
的类型参数(与 well-known String
类型无关,只是不幸的名称冲突)。使用语法着色,您应该看到这里的 String
是类型参数的颜色,与 String 类型的颜色不同。将此名称更改为其他任何名称,您就会意识到其中的困惑。
因此,如果我们将其重命名为 K
,问题就会变得很明显:问题 1“不覆盖任何内容”是因为您的泛型类型参数没有与 [=] 相同的 : Serializable
约束17=]接口中定义的方法。问题 2 源于此。
解决方案
Also, yes, I know that I could work around this issue in a couple of ways, like
- not specifying K on override, but on call (myHandler.findByID("myID"))
这一点实际上是问题的本质,而不是解决方法:在您的界面中定义一个泛型函数实际上使它成为该函数泛型契约的一部分。
实现必须在此处具有泛型类型参数。
如何修复它取决于您预期会发生什么。
如果您同意在您的实现中使用通用函数,那么保留您声明的接口,但接受实现必须处理 K
的所有可能值,这很可能这不是你想要的。
如果你想在你的实现中定义一个特定的类型,K
应该是接口定义的一部分(而不是接口方法),并且方法本身不应该有类型参数,而只是接受来自界面的现有K
:
interface FindByID<E, K : Serializable> {
fun findByID(id: K): E
}
我想使用将由其实现指定的通用接口 类,总而言之,这一切都运行良好,例如:
interface Remove <E> { fun remove(entity: E) }
class MyHandler : Remove <MyClass> {
override fun remove(entity: MyClass) { */do stuff/* }
}
但是,我有一个案例(到目前为止,期待更多)我希望函数本身是通用的。写接口完全没问题:
interface FindByID <E> { fun <K : Serializable> findByID(id: K): E }
我需要 K 是可序列化的,因为这是我需要调用的某些函数的要求。
编译器似乎不同意我的实现尝试。当我这样做时:
override fun <String> findByID(id: String): User {
return someFunction(User::class.java, id) as User
}
我遇到两个编译器错误:
- 什么都不覆盖
id
不可序列化
然而,当我从签名中删除 override
和 <String>
时,它工作正常。这意味着 String
是可序列化的,我的研究也表明了这一点。
这里似乎有什么问题?
此外,是的,我知道我可以通过多种方式解决这个问题,例如
- 使接口函数接受
Serializable
而不是<K : Serializable>
- 未在覆盖时指定
K
,但在调用时指定 (myHandler.findByID<String>("myID")
) - “重新路由”调用,在 MyHandler 中实现(而不是覆盖)一个接受字符串的函数,然后在内部调用被覆盖的函数
虽然愿意接受建议,但我对解决方法不太感兴趣,但更愿意了解并(如果可能)解决实际问题,或者至少知道它无法完成,所以我可以考虑到这一点用于规划
当前问题
根据您当前的 <String>
声明,您并没有像您想象的那样专门化类型参数。您实际上在做的是声明一个恰好命名为 String
的类型参数(与 well-known String
类型无关,只是不幸的名称冲突)。使用语法着色,您应该看到这里的 String
是类型参数的颜色,与 String 类型的颜色不同。将此名称更改为其他任何名称,您就会意识到其中的困惑。
因此,如果我们将其重命名为 K
,问题就会变得很明显:问题 1“不覆盖任何内容”是因为您的泛型类型参数没有与 [=] 相同的 : Serializable
约束17=]接口中定义的方法。问题 2 源于此。
解决方案
Also, yes, I know that I could work around this issue in a couple of ways, like
- not specifying K on override, but on call (myHandler.findByID("myID"))
这一点实际上是问题的本质,而不是解决方法:在您的界面中定义一个泛型函数实际上使它成为该函数泛型契约的一部分。 实现必须在此处具有泛型类型参数。
如何修复它取决于您预期会发生什么。
如果您同意在您的实现中使用通用函数,那么保留您声明的接口,但接受实现必须处理 K
的所有可能值,这很可能这不是你想要的。
如果你想在你的实现中定义一个特定的类型,K
应该是接口定义的一部分(而不是接口方法),并且方法本身不应该有类型参数,而只是接受来自界面的现有K
:
interface FindByID<E, K : Serializable> {
fun findByID(id: K): E
}