在下面的代码中,第一行如期编译并没有结果执行。第二个也将编译(不应编译)并抛出StringIndexOutOfBoundsException。为什么?
scala> 3 to 4 foreach (_ => "a")
scala> 3 to 4 foreach ("a")
java.lang.StringIndexOutOfBoundsException: String index out of range: 3
at java.lang.String.charAt(String.java:658)
at scala.collection.immutable.StringLike.apply(StringLike.scala:56)
at scala.collection.immutable.StringLike.apply$(StringLike.scala:56)
at scala.collection.immutable.WrappedString.apply(WrappedString.scala:34)
at scala.collection.immutable.WrappedString.apply(WrappedString.scala:34)
at scala.collection.immutable.Range.foreach(Range.scala:158)
... 28 elided
答案 0 :(得分:2)
因为字符串total
在索引"a"
或索引3
上没有任何元素。将字符串更改为4
,它将不会引发该错误。将"abcde"
更改为foreach
以查看结果。
答案 1 :(得分:2)
在您的代码段中,gem install bundler
实际上是一个函数。不相信我吗?让我们一起去兔子洞吧。
"a"
是"a"
。从Scala的角度来看,这种类型非常无聊。因此Scala具有implicit conversion类型的WrappedString
类型,您的代码正在调用该类型。现在,如果我们跟随父母的足迹,我们会看到
java.lang.String
因此WrappedString <: IndexedSeq[Char] <: immutable.Seq[Char] <: Seq[Char]
<: PartialFunction[Int, Char] <: (Int) => Char
实际上是WrappedString
的子类。我们可以在the docs for Seq
查看序列的另一种方法是使用
(Int) => Char
中的PartialFunction
序列元素类型的值。Int
序列的方法在从isDefinedAt
到0
的时间间隔内返回true。
因此,字符串(实际上是任何序列)的行为都类似于一个函数,该函数采用单个整数索引并返回序列中的该位置。因此,当您尝试在字符串中获得第三个(随后是第四个)位置时,会出现错误。
作为一般建议,我使用一个非常方便的编译器选项对此进行了解密。如果您将代码放在一个最小单例包装的文件中,就像这样
length
然后用object A {
3 to 4 foreach ("a")
}
“编译”它。您会看到类似这样的输出
scalac -print filename.scala
重要的一行就在[[syntax trees at end of cleanup]] // test.scala
package <empty> {
object A extends Object {
def <init>(): A.type = {
A.super.<init>();
RichInt.this.to$extension0(scala.this.Predef.intWrapper(3), 4).foreach(scala.this.Predef.wrapString("a"));
()
}
}
}
调用之后。
super
我们看到发生了一些怪异的魔术,从而使RichInt.this.to$extension0(scala.this.Predef.intWrapper(3), 4).foreach(scala.this.Predef.wrapString("a"));
语法起作用,然后稍后在行上看到3 to 4
,这表明我之前链接的scala.this.Predef.wrapString
转换在这里被隐式调用。从那里开始,只需查看超类,直到找到所需的内容即可。