【问题标题】:Scala factory for generic types using the apply method?使用 apply 方法的泛型类型的 Scala 工厂?
【发布时间】:2016-09-25 14:51:10
【问题描述】:

假设我有以下 trait,它定义了一个接口并接受了几个类型参数...

trait Foo[A, B] {

    // implementation details not important

}

我想将伴随对象用作特征的具体实现的工厂。我还想强制用户使用Foo 接口而不是子类所以我将具体实现隐藏在伴随对象中,如下所示:

object Foo {

  def apply[A, B](thing: Thing): Foo[A, B] = {
    ???
  }

  private case class FooImpl[A1, B1](thing: Thing) extends Foo[A1, B1]

  private case class AnotherFooImpl[A2, B1](thing: Thing) extends Foo[A2, B1]

}

我希望能够按如下方式使用工厂:

val foo = Foo[A1, B1](thing)  // should be an instance of FooImpl

val anotherFoo = Foo[A2, B1](thing)  // should be an instance of AnotherFooImpl

如何实现apply 方法来实现这一点?这个SO post 似乎很接近目标。

【问题讨论】:

  • A1A2是什么关系?遗产?没有关系?

标签: scala generics


【解决方案1】:

怎么样:

trait Foo[A, B]
trait Factory[A, B] {
  def make(thing: Thing): Foo[A, B]
}

class Thing

object Foo {
def apply[A, B](thing: Thing)(implicit ev: Factory[A, B]) = ev.make(thing)

private case class FooImpl[A, B](thing: Thing) extends Foo[A, B]
private case class AnotherFooImpl[A, B](thing: Thing) extends Foo[A, B]

implicit val fooImplFactory: Factory[Int, String] = new Factory[Int, String] {
  override def make(thing: Thing): Foo[Int, String] = new FooImpl[Int, String](thing)
}

implicit val anotherFooImplFactory: Factory[String, String] = new Factory[String, String] {
  override def make(thing: Thing): Foo[String, String] = new AnotherFooImpl[String, String](thing)
}

现在:

def main(args: Array[String]): Unit = {
  import Foo._

  val fooImpl = Foo[Int, String](new Thing)
  val anotherFooImpl = Foo[String, String](new Thing)

  println(fooImpl)
  println(anotherFooImpl)
}

产量:

FooImpl(testing.X$Thing@4678c730)
AnotherFooImpl(testing.X$Thing@c038203)

【讨论】:

  • 非常酷!您是否出于某种原因使用以下类型参数: FooImpl[A1,B1] AnotherFooImpl[A2,B1]?他们可能真的是对的吗?例如:可能只是 FooImpl[A,B] & AnotherFooImpl[A,B]
  • @Samar 我已经解决了这个问题,感谢您让我注意到。他们只是为了测试和玩耍。它们通常是 AB,它们不绑定到任何类型。
【解决方案2】:

使用TypeTags(克服类型参数的擦除),我们可以根据传递给apply方法的类型参数调用相应的隐藏实现,如下所示。它正确地实例化了各自的实现,但是Foo 的类型信息丢失了,实际上它会像_202 这样的垃圾?我不知道为什么会发生这种情况以及如何为 Foo 保留正确的类型。也许有人可以对此有所了解。

trait Foo[A,B]
object Foo {
   def apply[A: TypeTag, B: TypeTag](thing: Thing) = 
    if(typeTag[A] == typeTag[Int])  
      FooImpl(thing) 
    else if(typeTag[A] == typeTag[String]) 
      AnotherFooImpl(thing) 
    else 
      new Foo[Double,Double] {}

   private case class FooImpl(thing: Thing) extends Foo[Int, String]
   private case class AnotherFooImpl(thing: Thing) extends Foo[String, String]
  } 

Foo[Int,String](new Thing) // Foo[_202, _203] = FooImpl($sess.cmd123$Thing@50350b75)

The actual types for _203 and _203 are: ???
// type _203 >: String with _201, type _202 >: Int with _200 


Foo[String,String](new Thing) //Foo[_202, _203] = AnotherFooImpl($sess.cmd123$Thing@51d80d6)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多