Scala:如何确定一个类型是否可以为空

Scala: how to determine if a type is nullable

我有两个关于 Scala 中可空类型的问题:

  1. 假设我想定义一个新的 class:class myClass[T](x: T),并且我想确保 T 可以为 null。我该怎么做?

  2. 我想写一个函数 def myFunc(x: T)(不是上一个问题的一部分),如果 T 可以为空,我想执行一件事或者另一个如果没有。与上一个问题不同的是,这里我不想限制T,而是想知道它是否可以为空。我该怎么做?

在 Scala 中,所有扩展 AnyRef(相当于 Object)的类型都可以为空。大多数 scala 社区都避免使用空值,并且倾向于通过用 Option.

表示值的 existence/absence 来更明确

即使我们不经常在 Scala 中使用 null(支持 Option),您也可以使用

强制函数采用可空参数
def f[T <: AnyRef](x: T) = ???

1。使用 >: Null <: AnyRef 绑定:

@ def f[T >: Null <: AnyRef](arg: T): T = arg
defined function f

@ f(10)
cmd3.sc:1: inferred type arguments [Any] do not conform to method f's type parameter bounds [T >: Null <: AnyRef]
val res3 = f(10)
           ^
cmd3.sc:1: type mismatch;
 found   : Int(10)
 required: T
val res3 = f(10)
             ^
Compilation Failed

@ f("a")
res3: String = "a"

2。使用带默认值的隐式类型约束:

@ def isNullable[T](arg: T)(implicit sn: Null <:< T = null, sar: T <:< AnyRef = null): Boolean = sn != null && sar != null
defined function isNullable

@ isNullable(10)
res8: Boolean = false

@ isNullable("a")
res9: Boolean = true

这些类似于静态类型边界,不同之处在于它们是在隐式解析期间执行的而不是类型检查,因此如果您为它们提供默认值则允许失败(nulls 在这种情况下,没有双关语意:))

"Nullable" 表示它是 AnyRef 的子类(而不是 Nothing),因此,您可以强制 MyClass 只采用可空实例,如下所示:

case class MyClass[T <: AnyRef](t: T)

MyClass("hey")
MyClass[String](null)
MyClass(null)
// MyClass[Int](3) won't compile, because `Int` is primitive

要确定一个类型是否可为空,您可以提供生成可空性标记的隐式方法:

sealed trait Nullability[-T]
case object Nullable extends Nullability[AnyRef] {
  def isNull(t: Any): Boolean = t == null
}
case object NotNullable extends Nullability[AnyVal]
object Nullability {
  implicit def anyRefIsNullable[T <: AnyRef]: Nullability[T] = Nullable
  implicit def anyValIsNotNullable[T <: AnyVal]: Nullability[T] = NotNullable
}

def myFunc[T](t: T)(implicit nullability: Nullability[T]): Unit = {
  nullability match {
    case Nullable => 
      if (t == null) {
        println("that's a null")
      } else {
        println("that's a non-null object: " + t)
      }
    case NotNullable => println("That's an AnyVal: " + t)
  }
}

现在您可以按如下方式使用myFunc

myFunc("hello")
myFunc(null)
myFunc(42)

// outputs:
// that's a non-null object: hello
// that's a null
// That's an AnyVal: 42

如果您尝试在 Any 上使用 myFunc,这将无法编译,因为编译器将无法确定它是 AnyRef 还是 AnyVal ,这两个隐式方法会发生冲突。这样在编译时就可以保证我们不会不小心在Any上使用了myFunc,编译时无法判断可空性