How can you inherit a generic factory method?

后端 未结 2 1142
生来不讨喜
生来不讨喜 2021-01-12 10:14

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

class Persons extends ArrayBuffer[Person] {
// methods opera         


        
相关标签:
2条回答
  • 2021-01-12 10:33

    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
    }
    

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

    For example, you can do

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

    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.

    Edit:

    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
    }
    

    This allows you to do things like

    List(new Person("Joe", 38), new Person("Bob", 52)).names
    

    or

    val p = Persons(new Person("Jeff", 23))
    p += new Person("Sam", 20)
    

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

    0 讨论(0)
  • 2021-01-12 10:52

    Apart from anovstrup's solution, won't the example below do what you want?

    
    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))
    
    0 讨论(0)
提交回复
热议问题