Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I invoke the constructor of a Scala abstract type?

Tags:

scala

I'm trying to figure out how to invoke a constructor for a Scala abstract type:

class Journey(val length: Int)
class PlaneJourney(length: Int) extends Journey(length)
class BoatJourney(length: Int) extends Journey(length)

class Port[J <: Journey] {
  def startJourney: J = {
    new J(23) // error: class type required but J found
  }
}

Is this even feasible? I'm familiar with Scala manifests but I'm not clear how they could help here. Likewise I can't figure out how to do the same with a companion object's apply() constructor:

object Journey { def apply() = new Journey(0) }
object PlaneJourney { def apply() = new PlaneJourney(0) }
object BoatJourney { def apply() = new BoatJourney(0) }

class Port[J <: Journey] {
  def startJourney: J = {
    J() // error: not found: value J
  }
}

Any thoughts gratefully received!

like image 729
Alex Dean Avatar asked Sep 30 '11 22:09

Alex Dean


2 Answers

There is no direct way to invoke the constructor or access the companion object given only a type. One solution would be to use a type class that constructs a default instance of the given type.

trait Default[A] { def default: A }

class Journey(val length: Int)
object Journey {
  // Provide the implicit in the companion
  implicit def default: Default[Journey] = new Default[Journey] {
    def default = new Journey(0)
  }
}

class Port[J <: Journey : Default] {
  // use the Default[J] instance to create the instance
  def startJourney: J = implicitly[Default[J]].default
}

You will need to add an implicit Default definition to all companion objects of classes that support creation of a default instance.

like image 187
Moritz Avatar answered Oct 11 '22 18:10

Moritz


Your class needs an implicit constructor parameter to get the Manifest. Then you can call erasure to get the Class and call newInstance, which reflectively calls the nullary constructor if there is one.

class J[A](implicit m:Manifest[A]) {
  def n = m.erasure.newInstance()
}

new J[Object].n

As of Scala 2.10, the erasure property in the manifest is deprecated. def n = m.runtimeClass.newInstance() does the same thing, but without warnings.

like image 40
Kim Stebel Avatar answered Oct 11 '22 18:10

Kim Stebel