【问题标题】:creating a new instance of a type in scala在scala中创建一个类型的新实例
【发布时间】:2014-08-10 05:09:24
【问题描述】:

如果我有一个 C 类定义为

class C[A]

有没有办法在 C 中创建 A 的新实例?类似的东西

class C[A] {
  def f(): A = new A()
}

我知道,如果可能的话,您可能必须在某处指定构造函数参数,这很好。

如果不可能,是否有任何设计模式可以处理您希望创建类型的新实例的情况?

【问题讨论】:

  • 如果具体类型没有(无参数)构造函数,你建议会发生什么?
  • 理想情况下,您可以在 type 参数中指定它。所以我可以将 C 定义为class C[A(String, String)] 或其他东西。然后我必须用两个字符串参数调用 A。
  • @Raphael 不会,但class C[(String, String), A] 会。通过使用第二个类型参数,扩展下面的方法以将部分(或全部)构造函数参数传递给 make 方法非常简单。
  • 但是,C 的一种定义仅适用于一种特定的构造函数签名。这就是我的观点。
  • @Raphael 不是这样。 class C[Args, T](implicit e: Makeable[Args, T]) { ... }。每个 Makeable 实例只会根据需要解构 Args 类型以调用正确的构造函数。

标签: scala types


【解决方案1】:

您可以使用类型类来抽象实例化:

trait Makeable[T] {
   def make: T
}

class C[T: Makeable] {
   def f(): T = implicitly[Makeable[T]].make
}

例如,

implicit object StringIsMakeable extends Makeable[String] {
   def make: String = "a string"
}

val c = new C[String]
c.f // == "a string"

当您实例化 C 时,您需要显式或隐式地提供一个 Makeable,该 Makeable 将充当适当类型的工厂。当然,该工厂将负责在调用构造函数时提供任何构造函数参数。

或者,您可以使用 Manifest,但请注意,这种方法依赖于反射并且不是类型安全的:

class C[T: Manifest] {
   def f(): T = manifest[T].erasure.newInstance.asInstanceOf[T]
}

为了完整起见,您还可以轻松扩展此方法以将部分或全部构造函数参数传递给 make 方法:

trait Makeable[Args, T] { def make(a: Args): T }

class C[Args, T](implicit e: Makeable[Args, T]) {
   def f(a: Args): T = e.make(a)
}

// some examples
case class Person(firstName: String, lastName: String)

implicit val personFactory1 = new Makeable[(String, String), Person] {
   def make(a: (String, String)): Person = Person(a._1, a._2)
}
implicit val personFactory2 = new Makeable[String, Person] {
   def make(a: String): Person = Person(a, "Smith")
}

val c1 = new C[String, Person]
c1.f("Joe") // returns Person("Joe", "Smith")

val c2 = new C[(String, String), Person]
c2.f("John", "Smith") // returns Person("John", "Smith")

【讨论】:

    【解决方案2】:

    您可以要求一个隐式参数,如下所示:

    class A[T](implicit newT : T) { 
      val t = newT 
    } 
    

    当您实例化 A 时,您需要在范围内拥有所需类型的隐式工厂,例如以下作品:

    implicit def newSeq[T] = Seq[T]()                
    val a = new A[Seq[String]]                            
    

    如图所示:

    scala> a.t
    res22: Seq[String] = List()
    

    【讨论】:

      【解决方案3】:

      与@Raphael 的答案与案例类的apply 方法相同:

      class Container[A](contained: A)
      case class Person(name: String)
      case class PersonContainer(person: Person) extends Container[Person](person)
      implicit def _ = PersonContainer.apply _
      
      class Creator {
        def deserializeAndPackage[A, B <: Container[A]](data: Array[Byte])
                                 (implicit containerCreator: (A => B)): B = {
          val p = /* deserialize data as type of A */
          containerCreator(p)
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-25
        • 1970-01-01
        • 2016-08-25
        • 2023-02-11
        相关资源
        最近更新 更多