在 Scala 中测试数组是否为空
Testing an array for emptiness in Scala
问题不在于如何 测试数组是否为空(arr.length == 0
可以做到这一点)。相反,我的问题是,为什么
scala> Array().isEmpty
res1: Boolean = true
工作和
scala> val x = Array[String]()
x: Array[String] = Array()
scala> x.isEmpty
res2: Boolean = true
工作,但是
scala> val y = Array()
y: Array[Nothing] = Array()
scala> y.isEmpty
<console>:13: error: value isEmpty is not a member of Array[Nothing]
y.isEmpty
^
不会吧?
编辑:这个答案可能不正确。但我将其保留在这里以展示我是如何尝试调查此问题的。对我来说,这看起来像是编译器中的错误。
答案是 Array[T <: AnyRef]
存在的隐式转换
字符串是 AnyRef
,Nothing
不是 AnyRef
.
你怎么会自己发现这个?
在 IntelliJ 中,您可以在 isEmpty
下方看到一条灰色下划线
这意味着方法isEmpty
不是Array的方法,而是隐式的(class上的方法,从Array进行了隐式转换)。
现在,要发现隐式转换,只需单击 ctrl+q
所以当你跟着它的时候,你就到了源代码中的这一行-
implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T] = new ArrayOps.ofRef[T](xs)
这解释了当 T 扩展 AnyRef
时隐式转换是在 Array[T] 上
所以回到我们的案例-
String <: AnyRef
,但这不适用于 Nothing
正如@MichaelZajac 指出的那样,Nothing
是一切的子类型(它的对应物 Any
是一切的超类型)。特别是它也是 AnyRef
的子类型。事实上,还有一个更通用的 genericArrayOps
,它根本没有类型限制(例如 Array[Any]().isEmpty
有效)!允许您使用 isEmpty
的隐式转换应该 开始,但当然不会,即使显式调用转换很好。
@slouc 给出的 link 就是答案,即 Scala 编译器在进行隐式解析时以特殊方式处理 Nothing
,因为 Nothing
是 a 的默认下限进行类型推断时键入。
现在为什么不希望在隐式解析中考虑 Nothing
? Nothing
的棘手之处在于它是所有内容的子类型。这意味着如果 Scala 在任何时候将类型推断为 Nothing
,则每个隐式转换都会立即生效。这可能会隐藏类型错误(你永远不应该有 Nothing
的实例,但是当 Nothing
变成 Int
时……谁说的好呢?)。 (请注意,我希望真正破解编译器的人能够参与其中 confirm/deny/elaborate)
问题不在于如何 测试数组是否为空(arr.length == 0
可以做到这一点)。相反,我的问题是,为什么
scala> Array().isEmpty
res1: Boolean = true
工作和
scala> val x = Array[String]()
x: Array[String] = Array()
scala> x.isEmpty
res2: Boolean = true
工作,但是
scala> val y = Array()
y: Array[Nothing] = Array()
scala> y.isEmpty
<console>:13: error: value isEmpty is not a member of Array[Nothing]
y.isEmpty
^
不会吧?
编辑:这个答案可能不正确。但我将其保留在这里以展示我是如何尝试调查此问题的。对我来说,这看起来像是编译器中的错误。
答案是 Array[T <: AnyRef]
存在的隐式转换
字符串是 AnyRef
,Nothing
不是 AnyRef
.
你怎么会自己发现这个?
在 IntelliJ 中,您可以在 isEmpty
下方看到一条灰色下划线
这意味着方法isEmpty
不是Array的方法,而是隐式的(class上的方法,从Array进行了隐式转换)。
现在,要发现隐式转换,只需单击 ctrl+q
所以当你跟着它的时候,你就到了源代码中的这一行-
implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T] = new ArrayOps.ofRef[T](xs)
这解释了当 T 扩展 AnyRef
时隐式转换是在 Array[T] 上所以回到我们的案例-
String <: AnyRef
,但这不适用于 Nothing
正如@MichaelZajac 指出的那样,Nothing
是一切的子类型(它的对应物 Any
是一切的超类型)。特别是它也是 AnyRef
的子类型。事实上,还有一个更通用的 genericArrayOps
,它根本没有类型限制(例如 Array[Any]().isEmpty
有效)!允许您使用 isEmpty
的隐式转换应该 开始,但当然不会,即使显式调用转换很好。
@slouc 给出的 link 就是答案,即 Scala 编译器在进行隐式解析时以特殊方式处理 Nothing
,因为 Nothing
是 a 的默认下限进行类型推断时键入。
现在为什么不希望在隐式解析中考虑 Nothing
? Nothing
的棘手之处在于它是所有内容的子类型。这意味着如果 Scala 在任何时候将类型推断为 Nothing
,则每个隐式转换都会立即生效。这可能会隐藏类型错误(你永远不应该有 Nothing
的实例,但是当 Nothing
变成 Int
时……谁说的好呢?)。 (请注意,我希望真正破解编译器的人能够参与其中 confirm/deny/elaborate)