【发布时间】:2019-11-26 17:07:24
【问题描述】:
在 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> 和 P subtypeOf Bar<Any> 派生出 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 sn-p 说明了我的意思:
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,因为我们只需要在单一类型 Nothing 上评估 Bar:
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")))
这在没有更高种类的情况下有效,Java 的 super 就足够了。但是,正如上面已经提到的,Kotlin 在使用站点似乎没有像 >: / super / lower type bounds 这样的东西。
问题
除了将foo 移动到FooImpl,即将Foo 和FooImpl 合并到一个大文件中,还有什么可以做的吗?
【问题讨论】:
标签: generics kotlin types higher-kinded-types