的新手,我见过在 Scala 中连接字符串的代码,如下所示:
"test " ++ "1"
我已经测试过了,它也用 Scala 文档编写
"test " + "1"
所以我的理解是,+
就像Java字符串+
++
但功能更强大,可以接受更多类型的参数。此外,++
似乎对列表等其他事物具有通用性。我想知道我的理解是否正确。还有其他区别吗?什么时候应该只为了字符串连接而一个?
scala.Predef
看看到底发生了什么,会很有帮助。
如果你在那里检查,你会发现 Scala 中的String
只是 java.lang.String
的别名。换句话说,String
上的+
方法被转换为Java的+
运算符。
所以,如果一个Scala String
只是一个Java String
,你可能会问++
方法是如何存在的。(好吧,至少我会问。答案是,wrapString
方法提供了从 String
到 WrappedString
的隐式转换,这也是Predef
.
请注意,++
采用任何GenTraversableOnce
实例,并将该实例中的所有元素添加到原始WrappedString
中。(请注意,文档错误地指出该方法返回WrappedString[B]
。这必须是不正确的,因为WrappedString
不接受类型参数。你会得到的要么是String
(如果你添加的东西是Seq[Char]
(要么是一些IndexedSeq[Any]
(如果不是(。
以下是一些示例:
如果将String
添加到List[Char]
,则会得到一个字符串。
scala> "a" ++ List('b', 'c', 'd')
res0: String = abcd
如果向List[String]
添加String
,则会得到一个IndexedSeq[Any]
。事实上,前两个元素是Char
s,但后三个是String
s,如后续电话所示。
scala> "ab" ++ List("c", "d", "e")
res0: scala.collection.immutable.IndexedSeq[Any] = Vector(a, b, c, d, e)
scala> res0 map ((x: Any) => x.getClass.getSimpleName)
res1: scala.collection.immutable.IndexedSeq[String] = Vector(Character, Character, String, String, String)
最后,如果您使用++
向String
添加String
,则会返回一个String
。这样做的原因是WrappedString
继承自IndexedSeq[Char]
,所以这是一种向Seq[Char]
添加Seq[Char]
的复杂方式,它会给你一个Seq[Char]
。
scala> "abc" + "def"
res0: String = abcdef
正如 Alexey 所指出的,这些都不是一个非常微妙的工具,所以你最好使用字符串插值或StringBuilder
,除非有充分的理由不这样做。
String
是一个TraversableLike
,这意味着它可以分解成一系列元素(字符(。这就是++
的来源,否则你不能在字符串上做++
。 ++
只有在它的右侧(或该函数的参数(是可分解类型(或可遍历(时才有效。
现在String
如何成为TraversableLike
?这是Predef
中定义的隐式发挥作用的地方。其中一个隐式将普通String
转换为WrappedString
,其中WrappedString.canBuildFrom
拥有所有基本上以这种方式工作的胶水:
WrappedString.canBuildFrom
-> StringBuilder
-> StringLike
-> IndexedSeqOptimized
-> IndexedSeqLike
-> SeqLike
-> IterableLike
-> TraversableLike
由于 Predef 中定义的隐式已经在范围内,因此可以编写如下代码:
"test " ++ "1"
现在您的问题:
我想知道我的理解是否正确。 还有其他差异吗?
是的,你的理解是正确的方向。
什么时候应该只为了字符串连接而一个?
对于字符串连接,显然"test " + "1"
是创建更少的对象和更少的函数调用。但是,我总是更喜欢这样的字符串插值:
val t1 = "test"
val t2 = "1"
val t3 = s"$t1 $t2"
哪个更具可读性。
有关更多详细信息:
- Scala 源代码:https://github.com/scala/scala
- http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html
问题是,从所以我的理解是+就像Java字符串+,但++更强大,可以接受更多类型的参数
这个意义上说,+
on Strings更强大:它可以接受任何参数,就像在Java中一样。这通常被认为是一个错误功能(特别是因为它也适用于右侧的字符串(,但我们几乎坚持使用它。 正如您所说,++
是一种通用的收集方法,并且更安全("test " ++ 1
不会编译(。
什么时候应该只为了字符串连接而一个?
我更喜欢+
.但是,对于许多(我什至会说大多数(用法,您想要的两者都不是:使用字符串插值代替。
val n = 1
s"test $n"
当然,当从许多部分构建字符串时,请使用 StringBuilder
.
"更强大",但它通常用作连接/追加操作。 但是,它不执行分配。 IE listX ++ y
将附加到 listX,但i++
不会递增整数 i(因为这分配给变量而不是突变(。
至少这是我的理解。 我不是 Scala 专家。
在 scala.Predef
中有一个从 String
到 StringOps
的隐式转换。++
方法在 StringOps
类中定义。因此,每当您执行str1 ++ str2
时,scala编译器本质上(从编码人员的角度来看(将str1
包装在StringOps
中并调用++
方法StringOps
。注意StringOps
本质上是一种IndexedSeq
,所以++
算子非常灵活,例如
"Hello, " ++ "world!" //results in "Hello, world" with type String
"three" ++ (1 to 3) //results in Vector('t', 'h', 'r', 'e', 'e', 1, 2, 3) with type IndexedSeq[AnyVal]