前几天想用scala统计一个字符串中一个字符出现的次数,于是到scala官网上去搜String类的API,结果有点意外。在API DOCS如下一段:
Automatic imports
Identifiers in the scala package and the scala.Predef object are always in scope by default.
Some of these identifiers are type aliases provided as shortcuts to commonly used classes. For example, List is an alias for scala.collection.immutable.List.
Other aliases refer to classes provided by the underlying platform. For example, on the JVM, String is an alias for java.lang.String.
scala的String类其实就是java的String。作为JVM方言,这也是情理之中的事。自然在scala官网上不会有String的API介绍。换个思路,统计字符次数的方法一般都叫count,于是用scala+string+count在网上一搜,很快就找到想要的东西。
scala> "hello".count(_=='l')
res0: Int = 2
转念一想,java的String类印象中没有count方法啊。于是又到java官网查了String API,的确没有。
这是怎么回事呢?让我们先把scala华丽的外壳打开,看看里面到底发生了什么:
scala> :settings -Xprint:typer
scala> "hello".count(_=='l')
[[syntax trees at end of typer]] // <console>
package $line5 {
object $read extends scala.AnyRef {
def <init>(): $line5.$read.type = {
$read.super.<init>();
()
};
object $iw extends scala.AnyRef {
def <init>(): type = {
$iw.super.<init>();
()
};
object $iw extends scala.AnyRef {
def <init>(): type = {
$iw.super.<init>();
()
};
private[this] val res2: Int = scala.Predef.augmentString("hello").count(((x$1: Char) => x$1.==('l')));
<stable> <accessor> def res2: Int = $iw.this.res2
}
}
}
}
[[syntax trees at end of typer]] // <console>
package $line5 {
object $eval extends scala.AnyRef {
def <init>(): $line5.$eval.type = {
$eval.super.<init>();
()
};
lazy private[this] var $result: Int = _;
<stable> <accessor> lazy def $result: Int = {
$eval.this.$result = $line5.$read.$iw.$iw.res2;
$eval.this.$result
};
lazy private[this] var $print: String = _;
<stable> <accessor> lazy def $print: String = {
$eval.this.$print = {
$line5.$read.$iw.$iw;
"res2: Int = ".+(scala.runtime.ScalaRunTime.replStringOf($line5.$read.$iw.$iw.res2, 1000))
};
$eval.this.$print
}
}
}
res2: Int = 2
其中最关键的一句是:
val res2: Int = scala.Predef.augmentString("hello").count(((x$1: Char) => x$1.==('l')));
String对象hello并不是直接调用的count方法,而是经过了一次转换。首先String类对象作为参数传给了Predef.augmentString,然后再调用count方法。
继续到scala官网上看看Predef.augmentString是何方神圣:
implicit def augmentString(x: String): StringOps
原来是个隐式转换函数,把String类转成StringOps类。而StringOps类就有count方法:
def count(p: (Char) ⇒ Boolean): Int
Counts the number of elements in the traversable or iterator which satisfy a predicate.
真相终于大白。