creating a new instance of a type in scala

11,030

Solution 1

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

Solution 2

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

Solution 3

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)
  }
}
Share:
11,030

Related videos on Youtube

Aaron Yodaiken
Author by

Aaron Yodaiken

John Jay Scholar of Economics at Columbia. Love coding, designing, and learning to make the world a better place. Learn more about what I’m up to at www.aaronyodaiken.com.

Updated on May 24, 2022

Comments

  • Aaron Yodaiken
    Aaron Yodaiken almost 2 years

    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?