Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

creating a new instance of a type in scala

Tags:

types

scala

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?

like image 223
Aaron Yodaiken Avatar asked Mar 14 '11 18:03

Aaron Yodaiken


2 Answers

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")
like image 116
Aaron Novstrup Avatar answered Sep 28 '22 04:09

Aaron Novstrup


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()
like image 31
Raphael Avatar answered Sep 28 '22 04:09

Raphael