可以让 scala 需要一个非 Nothing 的泛型方法参数并返回类型安全

use*_*956 2 generics scala

在 Scala 中,如果泛型方法的调用者省略了显式指定类型参数,那么糟糕的事情就会大打折扣。例如:

class Expression[+T] // Will have eval():T method, so +T

class NothingTest {

  def makey[T](): Expression[T] = null

  def needsBool(b: Expression[Boolean]): Unit = {}

  var b: Expression[Boolean] = null
  var n = makey()  // : Expression[Nothing]

  b=n  // Yikes.

  needsBool(n)  // :-/ Supplied Expression[Nothing] ... not a Expression[Nothing]
}
Run Code Online (Sandbox Code Playgroud)

应该makey()(eg makey[Boolean]())提供一个类型参数,但是在这种情况下我忘记了程序已编译(顺便说一下,这非常容易做到)。

该程序最终将失败needsBool(省略了实现),它没有接收到一个Expression[Booolean]对象 - 它反而得到了一个Expression[Nothing]对象。Scala 的文档说 Nothing 是所有类型的子类,这看起来异常粗鲁,并且无论出现在哪里都肯定会破坏类型安全。

所以,为了重新引入一些类型安全,我可以:

  • 防止makey返回 Expression[Nothing] 但要求提供类型参数?(我怀疑不是),或
  • 阻止needsBool接收表达式[Nothing]?

在编译时。


更新:

更完整的(编译但运行时失败的示例):

class Expression[+T](val value:T){ 
  def eval:T = value
}

class NothingTest {

  def makey[T](): Expression[T] = new Expression[String]("blah").asInstanceOf[Expression[T]]

  def needsBool(b: Expression[Boolean]): Unit = {
    val boolval = b.eval  // Explode! String is not a Boolean
    println(boolval)
  }

  var b: Expression[Boolean] = null
  var n = makey()  // : Expression[Nothing]. You're suppose to supply a type, but forgot.
  b=n  // Yikes.
  needsBool(n)  // :-/ Supplied Expression[Nothing]
}
Run Code Online (Sandbox Code Playgroud)

Luk*_*itz 7

我找到了一个有点hacky的解决方案,但它有效。创建一个NotNothing在其类型参数中逆变的类型,然后为Any和提供一个隐式对象Nothing。现在,如果您尝试使用的值NotNothingNothing编译器会抱怨歧义。案例:

sealed trait NotNothing[-T] 

object NotNothing {
  implicit object YoureSupposedToSupplyAType extends NotNothing[Nothing]
  implicit object notNothing extends NotNothing[Any] 
}
Run Code Online (Sandbox Code Playgroud)

然后makey使用以下NotNothing类型约束您的函数:

def makey[T : NotNothing]() = { ... }
Run Code Online (Sandbox Code Playgroud)

瞧,现在如果你忘记提供一个类型,你会得到一个编译时错误!