Java 如何处理 Scala 类型中的协变性

How to deal with Covariancy in Scala types from Java

在我们的 Scala 项目中,我们使用包含协变类型参数的类型来传达决策。

sealed trait ArbiterResponse[+Tasks]

object Reject extends ArbiterResponse[Nothing]

trait Proceed[Tasks] extends ArbiterResponse[Tasks]
object Proceed extends Proceed[Nothing]
case class ProceedConditionally[Tasks](tasks : Tasks) extends Proceed[Tasks]

当从 Java 使用它时,我收到一个错误,指出 ArbiterResponse<SomeTask> 无法转换为 Reject$ in:

final ArbiterResponse<SomeTask> response = ???
if (task instanceof Reject$) { ... }

error: incompatible types: ArbiterResponse cannot be converted to Reject$

我该如何解决这个问题?我想这是因为 Java 编译器不知道 co-/contravariance。我通过添加描述 ArbiterResponse 响应类型的标志来解决这个问题。但我不太喜欢这个解决方案,因为它是一些手动的、容易出错的工作。谁有更好的主意?

sealed trait ArbiterResponse[+Tasks] {
  def isRejected: Boolean
  def isProceed: Boolean
  def isProceedConditionally: Boolean
}

object Reject extends ArbiterResponse[Nothing] {
  def isRejected = true
  def isProceed = false
  def isProceedConditionally = false
}

trait Proceed[Tasks] extends ArbiterResponse[Tasks] {
  def isRejected = false
  def isProceed = true
  def isProceedConditionally = false
}
object Proceed extends Proceed[Nothing]

case class ProceedConditionally[Tasks](tasks : Tasks) extends Proceed[Tasks] {
  override def isProceedConditionally = true
}

在大多数情况下从 java 调用 scala 代码是相当棘手的。但无论如何,如果你想通过类型转换的方式,那么你可以这样做:

    for (Object response : Arrays.asList(new ProceedConditionally("asdf"), Reject$.MODULE$)) {
        if(Reject$.class.isInstance(response)){
            Reject$ cast = Reject$.class.cast(response);
            System.out.println(cast);
        }else if(Proceed.class.isInstance(response)){
            System.out.println("Proceed");
        }else{
            System.out.println("Nothing: "+response.getClass());
        }
    }

打印

Proceed

prac.Reject$@266474c2

但它可能很快就会因不同的 Scala 版本而崩溃,或者很难让它正确。一种方法可能是通过访问者模式方式。不完全令人满意,但可能会有所帮助

sealed trait ArbiterResponse[+Tasks]{
  def doSomething
}

object Reject extends ArbiterResponse[Nothing]{
  def doSomething = ???
}

trait Proceed[Tasks] extends ArbiterResponse[Tasks]
object Proceed extends Proceed[Nothing]{
  def doSomething = ???
}
case class ProceedConditionally[Tasks](tasks : Tasks) extends Proceed[Tasks]{
  def doSomething = println(tasks)
}