假设你有一个Person类,并通过扩展例如为它创建一个集合类。 ArrayBuffer:
class Persons extends ArrayBuffer[Person] {
// methods operation on the collection
}
现在,使用ArrayBuffer,可以在配对对象上使用apply()方法创建一个集合,例如:
ArrayBuffer(1, 2, 3)
你希望能够对人物做同样的事情,例如:
Persons(new Person("John", 32), new Person("Bob", 43))
我的第一个直觉是扩展ArrayBuffer伴侣对象并免费获取apply()方法。但似乎你无法扩展对象。 (我不太清楚为什么。)
下一个想法是使用调用apply的apply()方法创建一个Persons对象 ArrayBuffer的方法:
object Persons {
def apply(ps: Person*) = ArrayBuffer(ps: _*)
}
但是,这会返回一个ArrayBuffer [Person]而不是Persons。
在scaladoc和ArrayBuffer的源代码中进行了一些挖掘之后,我提出了以下内容,我认为这会使Persons对象从GenericCompanion继承apply():
编辑:
object Persons extends SeqFactory[ArrayBuffer] {
def fromArrayBuffer(ps: ArrayBuffer[Person]) = {
val persons = new Persons
persons appendAll ps
persons
}
def newBuilder[Person]: Builder[Person, Persons] = new ArrayBuffer[Person] mapResult fromArrayBuffer
}
但是,它会显示以下错误消息:
<console>:24: error: type mismatch;
found : (scala.collection.mutable.ArrayBuffer[Person]) => Persons
required: (scala.collection.mutable.ArrayBuffer[Person(in method newBuilder)])
=> Persons
def newBuilder[Person]: Builder[Person, Persons] = new ArrayBuffer[Perso
n] mapResult fromArrayBuffer
^
也许这应该让我无法继续前进,但是我很开心学习Scala,我真的很想让它发挥作用。请告诉我,如果我走错了路。 :)
答案 0 :(得分:3)
您可以使用pimp my library pattern,而不是直接扩展ArrayBuffer[Person]
。我们的想法是让Persons
和ArrayBuffer[Person]
完全互换。
class Persons(val self: ArrayBuffer[Person]) extends Proxy {
def names = self map { _.name }
// ... other methods ...
}
object Persons {
def apply(ps: Person*): Persons = ArrayBuffer(ps: _*)
implicit def toPersons(b: ArrayBuffer[Person]): Persons = new Persons(b)
implicit def toBuffer(ps: Persons): ArrayBuffer[Person] = ps.self
}
Persons伴侣对象中的隐式转换允许您在拥有Persons引用时使用任何ArrayBuffer方法,反之亦然。
例如,您可以
val l = Persons(new Person("Joe"))
(l += new Person("Bob")).names
请注意,l
是Persons
,但您可以在其上调用ArrayBuffer.+=
方法,因为编译器会自动添加对Persons.toBuffer(l)
的调用。 +=
方法的结果是ArrayBuffer
,但您可以在其上调用Person.names
,因为编译器会调用Persons.toPersons
。
修改强>
您可以使用更高级别的类型来推广此解决方案:
class Persons[CC[X] <: Seq[X]](self: CC[Person]) extends Proxy {
def names = self map (_.name)
def averageAge = {
self map (_.age) reduceLeft { _ + _ } /
(self.length toDouble)
}
// other methods
}
object Persons {
def apply(ps: Person*): Persons[ArrayBuffer] = ArrayBuffer(ps: _*)
implicit def toPersons[CC[X] <: Seq[X]](c: CC[Person]): Persons[CC] =
new Persons[CC](c)
implicit def toColl[CC[X] <: Seq[X]](ps: Persons[CC]): CC[Person] =
ps.self
}
这允许您执行
之类的操作List(new Person("Joe", 38), new Person("Bob", 52)).names
或
val p = Persons(new Person("Jeff", 23))
p += new Person("Sam", 20)
请注意,在后一个示例中,我们在+=
上调用Persons
。这是可能的,因为Persons
“记住”基础集合类型,并允许您调用该类型中定义的任何方法(在这种情况下ArrayBuffer
,由于Persons.apply
的定义)。 / p>
答案 1 :(得分:1)
除了anovstrup的解决方案之外,下面的例子不会做你想要的吗?
case class Person(name: String, age: Int)
class Persons extends ArrayBuffer[Person]
object Persons {
def apply(ps: Person*) = {
val persons = new Persons
persons appendAll(ps)
persons
}
}
scala> val ps = Persons(new Person("John", 32), new Person("Bob", 43))
ps: Persons = ArrayBuffer(Person(John,32), Person(Bob,43))
scala> ps.append(new Person("Bill", 50))
scala> ps
res0: Persons = ArrayBuffer(Person(John,32), Person(Bob,43), Person(Bill,50))