generics - 针对特定情况 F 缺少 HKT 的解决方法对于一个 F
问题描述
Kotlin 中是否有任何方法可以通过在下面的最小示例中留下抽象来Foo
分离?Bar
defaultBar
sealed class Bar<out X> {
data class Hello<X>(val who: X): Bar<X>()
object Empty : Bar<Nothing>()
}
interface Foo {
fun defaultBar(): Bar<Nothing> = Bar.Empty
fun <P> foo(bar: Bar<P> = defaultBar()): Bar<P> = bar
}
fun main(args: Array<String>) {
println((object : Foo{}).foo(Bar.Hello("world")))
}
通过“单独”,我的意思是我想要Foo
并Bar
位于两个完全独立的文件中,因此它们不会相互提及。该方法foo
应该是“硬”的,并且应该保留在Foo
. 该方法defaultBar
应该是“微不足道的”,它可以在任何地方实现。Foo
我想同时提到两者的唯一地方Bar
应该是一些单独的类,它提供defaultBar
.
我试过的
在最初的几分钟里,以下尝试似乎很有希望:
sealed class Bar<out X> {
data class Hello<X>(val who: X): Bar<X>()
object Empty : Bar<Nothing>()
}
interface Foo<B>{
fun <P: B> defaultB(): P
fun <P: B> foo(bar: P = defaultB()): P = bar
}
object FooImpl : Foo<Bar<Any>> {
override fun <P: Bar<Any>> defaultB(): P = Bar.Empty
}
fun main(args: Array<String>) {
println(FooImpl.foo(Bar.Hello("world")))
}
不幸的是,Kotlin 不能从Bar<out X>
and P subtypeOf Bar<Any>
that派生出来Bar<Nothing> subtypeOf P
。
此外,我也不能写类似的东西
sealed class Bar<out X> {
data class Hello<X>(val who: X): Bar<X>()
object Empty : Bar<Nothing>()
}
interface Foo<B, N: B> {
fun defaultBar(): N
fun <P : B super N> foo(bar: Bar<P> = defaultBar()): Bar<P> = bar
}
object FooImpl : Foo<Bar<Any>, Bar<Nothing>> {
fun defaultBar(): Bar<Nothing> = Bar.Empty
}
fun main(args: Array<String>) {
println(FooImpl.foo(Bar.Hello("world")))
}
,因为除了不支持 HKT 之外,Kotlin 也不支持使用站点的下限类型。
(可选)我希望的都是可能的
以下 Scala 片段显示了我的意思:
sealed trait Bar[+X]
case class Hello(who: String) extends Bar[String]
case object Empty extends Bar[Nothing]
trait Foo {
val defaultBar: Bar[Nothing] = Empty
def foo[P](bar: Bar[P] = defaultBar): Bar[P] = bar
}
println((new Foo{}).foo(Hello("world")))
这可以重构为:
import language.higherKinds
sealed trait Bar[+X]
case class Hello(who: String) extends Bar[String]
case object Empty extends Bar[Nothing]
trait Foo[B[+_]] {
val defaultB: B[Nothing]
def foo[P](b: B[P] = defaultB): B[P] = b
}
object FooImpl extends Foo[Bar] {
val defaultB: Bar[Nothing] = Empty
}
println(FooImpl.foo(Hello("world")))
所以Foo
没有提到Bar
任何地方。
(可选)为什么我希望它应该是可能的
对于这种特殊情况,不需要完整的 HKT,因为我们只需要评估Bar
一种类型Nothing
:
sealed trait Bar[+X]
case class Hello(who: String) extends Bar[String]
case object Empty extends Bar[Nothing]
trait Foo[N] {
val defaultBar: N
def foo[P >: N](bar: P = defaultBar): P = bar
}
object FooInstance extends Foo[Bar[Nothing]] {
val defaultBar = Empty
}
println(FooInstance.foo(Hello("world")))
这在没有更高种类的情况下工作,Javasuper
就足够了。但是,正如上面已经提到的,Kotlin 在使用站点上似乎没有类似>:
//super
下限类型的东西。
问题
foo
除了移入FooImpl
,即合并Foo
并FooImpl
进入一个大文件之外,还有什么可以做的吗?
解决方案
super
您可以通过制作foo
扩展功能并编写B : P
而不是来模仿P super B
:
sealed class Bar<out X> {
data class Hello<out X>(val who: X) : Bar<X>()
object Empty : Bar<Nothing>()
}
interface Foo<out B> {
fun defaultBar(): B
}
fun <B : P, P> Foo<B>.foo(bar: P = defaultBar()): P = bar
object FooImpl : Foo<Bar<Nothing>> {
override fun defaultBar(): Bar<Nothing> = Bar.Empty
}
fun main() {
println(FooImpl.foo(Bar.Hello("world")))
}
推荐阅读
- bash - 尝试在 bash 的 for 循环中使用选择/案例
- c++ - 升级 .sln 文件后,“E225 __clrcall 不允许使用省略号参数的函数”
- winapi - 如何检测鼠标悬停在窗口的非客户端部分?
- javascript - 如何在 Google 日历中单击按钮时触发 Chrome 扩展程序?
- python - 使用 pyinstaller(4.5) 和 librdkafka 1.7.0 将 python 程序转换为 exe 应用程序时出现错误“confluent_kafka.libs are not found”
- reactjs - 导航栏不会根据路由条件在 app.js 中呈现。路由更改时不会触发 Useeffect
- javascript - MongoError:数据库名称不能包含字符'/'
- powerbi - Power BI - 基于切片器选择的动态矩阵行标题
- spring-mybatis - Mybatis返回HashMap
- php - 如何以larvel方式组合两个查询