,generics,kotlin,types,higher-kinded-types"/>

首页 > 解决方案 > 针对特定情况 F 缺少 HKT 的解决方法对于一个 F

问题描述

Kotlin 中是否有任何方法可以通过在下面的最小示例中留下抽象来Foo分离?BardefaultBar

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")))
}

通过“单独”,我的意思是我想要FooBar位于两个完全独立的文件中,因此它们不会相互提及。该方法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,即合并FooFooImpl进入一个大文件之外,还有什么可以做的吗?

标签: genericskotlintypeshigher-kinded-types

解决方案


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")))
}

推荐阅读