1

I am trying to create a generic method to create class instance by specified type parameter.

Code change conditions: * as minimum as possible changes to P1 - P3 classes its a legacy code an code below is just a prototype, ideally no changes at all to these classes.

See my code below.

trait P {
  def run() : Unit = ???
}

class P1 (cfg : String ) extends P {
  override def run() : Unit = {
    println("1")
  }
}

class P2 (cfg : String )extends P {
  override def run() : Unit = {
    println("2")
  }
}

class P3 (cfg : String ) extends P {
  override def run() : Unit = {
    println("3")
  }
}



  def executor[T <: P](cfg: String): Unit  = {
    new T(cfg).run()
  }

executor[P1]("someCfg")

executor[P2]("someCfg")

executor[P3]("someCfg")

Here is error I am getting:

Error:(26, 10) class type required but T found
    new T(cfg).run()
        ^
Error:(53, 10) class type required but T found
    new T(cfg).run()
        ^
1
  • 1
    You'll have to help the compiler. How about something like: def executor[T<:P](cfg: String)(fn: String => T): Unit = fn(cfg).run(). And then use it like executor("someCfg")(new P1(_)). Commented Oct 17, 2017 at 20:30

2 Answers 2

4

You can do this:

import scala.reflect.ClassTag

def executor[T <: P](cfg: String)(implicit tag: ClassTag[T]): Unit  = {
  tag.runtimeClass.getConstructor(classOf[String]).
    newInstance(cfg).asInstanceOf[T].run()
}

And use it:

executor[P1]("someCfg")
executor[P2]("someCfg")
executor[P3]("someCfg")

Reading http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html and simply searching for ClassTag and TypeTag will give you more information.

However, this is less safe than the solution given in the other answer: it'll fail at runtime if the class doesn't have a constructor taking String.

Sign up to request clarification or add additional context in comments.

Comments

3

Information about T is lost in runtime due to type erasure, therefore you cannot instantiate it.

In order to satisfy your requirements with minimal changes of P1 - P3 classes you would have to pass some kind of factory function to executor:

  def executor[T <: P](cfg: String)(fct: String => T): Unit = {
    fct(cfg).run()
  }

In this case calling side would change to this:

  executor("someCfg")(new P1(_))

  executor("someCfg")(new P2(_))

  executor("someCfg")(new P3(_))

with no changes to P1 - P3 classes.

Output:

1
2
3

3 Comments

I was hoping that there is some kind of magic trick so its can be by passed... thanks anyway
It is jvm limitation so I doubt that it can be avoided
It can, actually. See my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.