可能使scala需要非Nothing泛型方法参数并返回类型安全

时间:2016-12-30 22:09:30

标签: scala generics

在scala中,如果泛型方法的调用者省略显式指定类型参数,那么shit可以命中风扇。例如:

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]
}

假设makey()提供了一个类型参数(例如makey[Boolean]()),但是在这个例子中我忘记了,程序编译了(通过这样,很容易做到。)

程序最终将失败needsBool(实现省略),但没有收到Expression[Booolean]个对象 - 它改为Expression[Nothing]个对象。 Scala的文档说没有什么是所有类型的子类,这看起来格外粗鲁,并且肯定会在任何地方打破类型安全。

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

  • 阻止makey返回Expression [Nothing],但要求提供类型参数? (I suspect not),或者
  • 阻止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]
}

1 个答案:

答案 0 :(得分:4)

我发现了一个有点hacky的解决方案,但它确实有效。 创建一个在其类型参数中具有逆变的NotNothing类型,然后为AnyNothing提供隐式对象。 现在,如果您尝试将NotNothing的值与Nothing一起使用,编译器将会抱怨模糊性。一个很好的例子:

sealed trait NotNothing[-T] 

object NotNothing {
  implicit object YoureSupposedToSupplyAType extends NotNothing[Nothing]
  implicit object notNothing extends NotNothing[Any] 
}

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

def makey[T : NotNothing]() = { ... }

现在,如果您忘记提供类型,您将收到编译时错误!