如何在 Scala 中重载双冒号运算符?
How to overload two-colon operator in Scala?
我希望通过重载列表运算符和 Nil
在 Scala
中实现 builder pattern。但是显然没有用。
class SomeBuilder {
val sb : java.lang.StringBuffer = new java.lang.StringBuffer
def ::(str : java.lang.String): SomeBuilder = {
sb.append(str)
this
}
def Nil(): java.lang.String = {
sb.toString
}
}
object Hello extends App {
println( new SomeBuilder :: "aaa" :: "bbb" :: Nil )
}
为什么以及如何成功?
您可能会在 Scala spec
中找到
The associativity of an operator is determined by the operator's last character. Operators ending in a colon ‘:’ are right-associative. All other operators are left-associative.
稍后:
If there are consecutive infix operations e0;op1;e1;op2…opn;en
with operators op1,…,opn
of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as (…(e0;op1;e1);op2…);opn
. Otherwise, if all operators are right-associative, the sequence is interpreted as e0;op1;(e1;op2;(…opn;en)…)
.
这意味着你的语法
new SomeBuilder :: "aaa" :: "bbb" :: Nil
实际解释为
Nil.::("bbb").::("aaa").::(new SomeBuilder)
这样做是因为 ::
是函数式编程中传统上用于构建不可变 List
的运算符,而这正是那里所需的语义。所以如果你真的想使用 ::
你应该写这样的代码:
object Nil {
class RealBuilder(val sb: java.lang.StringBuilder) {
def ::(str: java.lang.String): RealBuilder = {
sb.append(str.reverse)
this
}
def ::(terminal: SomeBuilder): String = {
sb.reverse.toString
}
override def toString = sb.toString
}
override def toString = sb.toString
}
def ::(str: java.lang.String): RealBuilder = {
new RealBuilder(new java.lang.StringBuilder(str))
}
override def toString = ""
}
sealed trait SomeBuilder
object SomeBuilder extends SomeBuilder
然后
println(SomeBuilder :: "aaa" :: "bbb" :: Nil)
会起作用。但请注意这是多么低效:实际上你将每个字符串旋转了两次。您必须这样做,因为 ::
是右结合的,并且没有有效的 perpend
方法。
总结 您可以使用此语法,但是为此使用 ::
是一个非常糟糕的主意。如果你几乎使用任何其他东西,你会更好。
旁注 #1:使用 Nil
也是一个相当糟糕的主意,因为它会与 scala.collection.immutable.Nil
(即空 List
)。
旁注 #2:虽然现代 JVM 实现可以优化 synchronized
很多情况下你最好使用 java.lang.StringBuilder
instead of java.lang.StringBuffer
在这样的非多线程环境下
更新相同但 ++
所以主要问题是使用 ::
。如果您使用 ++
等其他运算符,它应该可以找到。
如果这样的语法
println(new SomeBuilder ++ "aaa1" ++ "bbb2" build)
或
println((new SomeBuilder ++ "aaa1" ++ "bbb2").build)
你没问题,你可以使用这样的代码:
class SomeBuilder {
val sb: StringBuilder = new StringBuilder
def ++(s: String): SomeBuilder = {
sb.append(s)
this
}
def build: String = sb.toString()
override def toString = sb.toString
}
如果您出于某种原因更喜欢更接近您的示例的内容,例如
println(new SomeBuilder ++ "aaa1" ++ "bbb2" ++ BuildString)
您可以使用这样的代码:
class SomeBuilder {
val sb: StringBuilder = new StringBuilder
def ++(s: String): SomeBuilder = {
sb.append(s)
this
}
def ++(terminator: BuildString): String = sb.toString()
override def toString = sb.toString
}
sealed trait BuildString
object BuildString extends BuildString
我希望通过重载列表运算符和 Nil
在 Scala
中实现 builder pattern。但是显然没有用。
class SomeBuilder {
val sb : java.lang.StringBuffer = new java.lang.StringBuffer
def ::(str : java.lang.String): SomeBuilder = {
sb.append(str)
this
}
def Nil(): java.lang.String = {
sb.toString
}
}
object Hello extends App {
println( new SomeBuilder :: "aaa" :: "bbb" :: Nil )
}
为什么以及如何成功?
您可能会在 Scala spec
中找到The associativity of an operator is determined by the operator's last character. Operators ending in a colon ‘:’ are right-associative. All other operators are left-associative.
稍后:
If there are consecutive infix operations
e0;op1;e1;op2…opn;en
with operatorsop1,…,opn
of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as(…(e0;op1;e1);op2…);opn
. Otherwise, if all operators are right-associative, the sequence is interpreted ase0;op1;(e1;op2;(…opn;en)…)
.
这意味着你的语法
new SomeBuilder :: "aaa" :: "bbb" :: Nil
实际解释为
Nil.::("bbb").::("aaa").::(new SomeBuilder)
这样做是因为 ::
是函数式编程中传统上用于构建不可变 List
的运算符,而这正是那里所需的语义。所以如果你真的想使用 ::
你应该写这样的代码:
object Nil {
class RealBuilder(val sb: java.lang.StringBuilder) {
def ::(str: java.lang.String): RealBuilder = {
sb.append(str.reverse)
this
}
def ::(terminal: SomeBuilder): String = {
sb.reverse.toString
}
override def toString = sb.toString
}
override def toString = sb.toString
}
def ::(str: java.lang.String): RealBuilder = {
new RealBuilder(new java.lang.StringBuilder(str))
}
override def toString = ""
}
sealed trait SomeBuilder
object SomeBuilder extends SomeBuilder
然后
println(SomeBuilder :: "aaa" :: "bbb" :: Nil)
会起作用。但请注意这是多么低效:实际上你将每个字符串旋转了两次。您必须这样做,因为 ::
是右结合的,并且没有有效的 perpend
方法。
总结 您可以使用此语法,但是为此使用 ::
是一个非常糟糕的主意。如果你几乎使用任何其他东西,你会更好。
旁注 #1:使用 Nil
也是一个相当糟糕的主意,因为它会与 scala.collection.immutable.Nil
(即空 List
)。
旁注 #2:虽然现代 JVM 实现可以优化 synchronized
很多情况下你最好使用 java.lang.StringBuilder
instead of java.lang.StringBuffer
在这样的非多线程环境下
更新相同但 ++
所以主要问题是使用 ::
。如果您使用 ++
等其他运算符,它应该可以找到。
如果这样的语法
println(new SomeBuilder ++ "aaa1" ++ "bbb2" build)
或
println((new SomeBuilder ++ "aaa1" ++ "bbb2").build)
你没问题,你可以使用这样的代码:
class SomeBuilder {
val sb: StringBuilder = new StringBuilder
def ++(s: String): SomeBuilder = {
sb.append(s)
this
}
def build: String = sb.toString()
override def toString = sb.toString
}
如果您出于某种原因更喜欢更接近您的示例的内容,例如
println(new SomeBuilder ++ "aaa1" ++ "bbb2" ++ BuildString)
您可以使用这样的代码:
class SomeBuilder {
val sb: StringBuilder = new StringBuilder
def ++(s: String): SomeBuilder = {
sb.append(s)
this
}
def ++(terminator: BuildString): String = sb.toString()
override def toString = sb.toString
}
sealed trait BuildString
object BuildString extends BuildString