在 scala 中创建类型的新实例

发布于 2024-10-21 22:02:47 字数 255 浏览 1 评论 0原文

如果我定义了一个类 C,

class C[A]

有什么方法可以在 C 中创建 A 的新实例吗?就像

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

我理解的那样,如果这是可能的,您可能必须在某处指定构造函数参数,这很好。

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

If I have a class C defined as

class C[A]

is there any way to create a new instance of A within C? Something like

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

I understand that, if this were possible, you'd probably have to specify the constructor arguments somewhere, and that's fine.

If it's not possible, are there any design patterns for dealing with the sort of situation where you'd like to create a new instance of a type?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

白昼 2024-10-28 22:02:48

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

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

You can demand an implicit parameter, like so:

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

All you need then is to have an implicit factory of the desired type in scope when you instanciate A, e.g. the following works:

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

As shown by:

scala> a.t
res22: Seq[String] = List()
德意的啸 2024-10-28 22:02:48

与 @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)
  }
}

The same as @Raphael's answer with a case class's apply method:

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)
  }
}
纸伞微斜 2024-10-28 22:02:47

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

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 来充当适当类型的工厂。当然,该工厂将负责在调用构造函数时提供任何构造函数参数。

或者,您可以使用 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")

You could use a type class to abstract instantiation:

trait Makeable[T] {
   def make: T
}

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

For example,

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

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

When you instantiate C, you'll need to provide, explicitly or implicitly, a Makeable that will act as a factory of the appropriate type. That factory, of course, would be responsible for supplying any constructor arguments when it invokes the constructor.

Alternatively, you could use a Manifest, but be warned that this approach relies on reflection and is not type safe:

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

For completeness, you can also easily extend this approach to pass some or all of the constructor parameters in to the make method:

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")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文