如何继承泛型工厂方法? [英] How can you inherit a generic factory method?

查看:51
本文介绍了如何继承泛型工厂方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您有一个 Person 类,并通过扩展为它创建一个集合类,例如数组缓冲区:

Say you have a class Person, and create a collection class for it by extending e.g. ArrayBuffer:

class Persons extends ArrayBuffer[Person] {
// methods operation on the collection
}

现在,使用 ArrayBuffer,可以在伴随对象上使用 apply() 方法创建一个集合,例如:

Now, with ArrayBuffer, can create a collection with the apply() method on the companion object, e.g.:

ArrayBuffer(1, 2, 3)

您希望能够对 Persons 执行相同的操作,例如:

You want to be able to do the same with Persons, e.g.:

Persons(new Person("John", 32), new Person("Bob", 43))

我的第一个直觉是扩展 ArrayBuffer 伴随对象并免费获得 apply() 方法.但是好像不能扩展对象.(我不太确定为什么.)

My first intuition here was to extend the ArrayBuffer companion object and getting the apply() method for free. But it seems that you can't extend objects. (I'm not quite sure why.)

下一个想法是创建一个 Persons 对象,该对象使用 apply() 方法调用 applyArrayBuffer 的方法:

The next idea was to create a Persons object with an apply() method that calls the apply method of ArrayBuffer:

object Persons {
    def apply(ps: Person*) = ArrayBuffer(ps: _*) 
}

然而,这将返回一个 ArrayBuffer[Person] 而不是一个 Persons.

However, this returns an ArrayBuffer[Person] and not a Persons.

在对 ArrayBuffer 的 scaladoc 和源代码进行一些挖掘之后,我想出了以下内容,我认为这会使 Persons 对象从 GenericCompanion 继承 apply():

After some digging in the scaladoc and source for ArrayBuffer, I came up with the following, which I thought would make the Persons object inherit apply() from GenericCompanion:

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
}

但是,它给出了以下错误消息:

However, it gives the following error message:

<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 的过程中很开心,我真的很想让它发挥作用.如果我走错了路,请告诉我.:)

Perhaps this should disencourage me from going further, but I'm having a great time learning Scala and I'd really like to get this working. Please tell me if I'm on the wrong track. :)

推荐答案

与其直接扩展 ArrayBuffer[Person],您可以使用 拉皮条我的图书馆模式.这个想法是让 PersonsArrayBuffer[Person] 完全可以互换.

Rather than extending ArrayBuffer[Person] directly, you can use the pimp my library pattern. The idea is to make Persons and ArrayBuffer[Person] completely interchangeable.

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 方法,反之亦然.

The implicit conversion in the Persons companion object allows you to use any ArrayBuffer method whenever you have a Persons reference and vice-versa.

例如,你可以这样做

val l = Persons(new Person("Joe"))
(l += new Person("Bob")).names

注意l是一个Persons,但是你可以在上面调用ArrayBuffer.+=方法,因为编译器会自动加入调用 Persons.toBuffer(l).+= 方法的结果是一个 ArrayBuffer,但是你可以在它上面调用 Person.names 因为编译器插入了一个对 Persons.toPersons.

Note that l is a Persons, but you can call the ArrayBuffer.+= method on it because the compiler will automatically add in a call to Persons.toBuffer(l). The result of the += method is an ArrayBuffer, but you can call Person.names on it because the compiler inserts a call to Persons.toPersons.

你可以用更高级的类型来概括这个解决方案:

You can generalize this solution with higher-kinded types:

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).

Note that in the latter example, we're calling += on a Persons. This is possible because Persons "remembers" the underlying collection type and allows you to call any method defined in that type (ArrayBuffer in this case, due to the definition of Persons.apply).

这篇关于如何继承泛型工厂方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆