Scala:如何确定一个类型是否可以为空
Scala: how to determine if a type is nullable
我有两个关于 Scala 中可空类型的问题:
假设我想定义一个新的 class:class myClass[T](x: T)
,并且我想确保 T
可以为 null。我该怎么做?
我想写一个函数 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
这些类似于静态类型边界,不同之处在于它们是在隐式解析期间执行的而不是类型检查,因此如果您为它们提供默认值则允许失败(null
s 在这种情况下,没有双关语意:))
"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
,编译时无法判断可空性
我有两个关于 Scala 中可空类型的问题:
假设我想定义一个新的 class:
class myClass[T](x: T)
,并且我想确保T
可以为 null。我该怎么做?我想写一个函数
def myFunc(x: T)
(不是上一个问题的一部分),如果T
可以为空,我想执行一件事或者另一个如果没有。与上一个问题不同的是,这里我不想限制T
,而是想知道它是否可以为空。我该怎么做?
在 Scala 中,所有扩展 AnyRef
(相当于 Object
)的类型都可以为空。大多数 scala 社区都避免使用空值,并且倾向于通过用 Option
.
即使我们不经常在 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
这些类似于静态类型边界,不同之处在于它们是在隐式解析期间执行的而不是类型检查,因此如果您为它们提供默认值则允许失败(null
s 在这种情况下,没有双关语意:))
"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
,编译时无法判断可空性