我有一个函数,它以Seq[_]
作为参数,并以Seq
作为val成员返回一个不可变的类实例。如果Seq
是可变的,我显然想要创建一个防御性副本,以保证我的返回类实例不能被修改。
此模式的最佳做法是什么?首先,我感到惊讶的是, 可能会使函数重载
def fnc(arg: immutable.Seq[_]) = ...
def fnc(arg: mutable.Seq[_]) = ...
我也可以模式匹配:
def fnc(arg: Seq[_]) = arg match {
case s: immutable.Seq[_] => { println("immutable"); s}
case s: mutable.Seq[_] => {println("mutable"); List()++s }
case _: ?
}
但我不确定_
案例。保证arg
是immutable.Seq
还是mutable.Seq
?我也不知道List()++s
是否是转换它的正确方法。我在SO上看过很多帖子,但其中大多数都是2.8或更早的帖子。
Scala-Collections是否“足够智能”,我可以随时(没有模式匹配)写List()++s
并且如果不可变则获得相同的实例,如果可变则获得深拷贝?
建议的方法是什么?
答案 0 :(得分:4)
如果你想同时支持,你将需要模式匹配。 Seq() ++
的代码不保证(作为其API的一部分),如果它是不可变的,它将不会复制其余部分:
scala> val v = Vector(1,2,3)
v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)
scala> Seq() ++ v
res1: Seq[Int] = List(1, 2, 3)
对于某些特殊情况,它可能会模式匹配,但您知道您想要的案例。所以:
def fnc[A](arg: Seq[A]): Seq[A] = arg match {
case s: collection.immutable.Seq[_] => arg
case _ => Seq[A]() ++ arg
}
您无需担心_
;这只是说你并不关心究竟是什么类型参数(不是你可以检查),如果你这样写,你不会:如果不可变则传递,否则复制。
答案 1 :(得分:1)
此模式的最佳做法是什么?
如果您想保证不变性,最佳做法是制作防御性副本,或者要求immutable.Seq
。
但我不确定_案例。是否保证arg是不可变的.Seq或mutable.Seq?
不一定,但我相信从collection.Seq
继承的每个标准库集合也都继承自这两者之一。但是,自定义集合理论上可以仅从collection.Seq
继承。有关模式匹配解决方案的改进,请参阅Rex的答案。
Scala-Collections是否“足够智能”,我可以随时(没有模式匹配)编写List()++ s,如果不可变则获得相同的实例,如果可变则获得深层复制?
它们似乎在某些案件中,但不在其他案件中,例如:
val immutableSeq = Seq[Int](0, 1, 2)
println((Seq() ++ immutableSeq) eq immutableSeq) // prints true
val mutableSeq = mutable.Seq[Int](0, 1, 2)
println((Seq() ++ mutableSeq) eq mutableSeq) // prints false
其中eq
是引用相等。请注意,上述内容也适用于List() ++ s
,但正如Rex指出的那样,它不适用于所有集合,例如Vector
。
答案 2 :(得分:0)
你当然可以超载!例如,这编译好:
object MIO
{
import collection.mutable
def f1[A](s: Seq[A]) = 23
def f1[A](s: mutable.Seq[A]) = 42
def f2(s: Seq[_]) = 19
def f2(s: mutable.Seq[_]) = 37
}
在REPL中:
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import rrs.scribble.MIO._; import collection.mutable.Buffer
import rrs.scribble.MIO._
import collection.mutable.Buffer
scala> f1(List(1, 2, 3))
res0: Int = 23
scala> f1(Buffer(1, 2, 3))
res1: Int = 42
scala> f2(List(1, 2, 3))
res2: Int = 19
scala> f2(Buffer(1, 2, 3))
res3: Int = 37