Instanciate concrete class a generic method in sca

2019-09-11 06:57发布

问题:

I have a generic method which a generic type parameter T which is a subclass of MyClass. Inside that method, I want to create e new instance of T, how can I do that?

This doesn't work (because of type erasure):

object Demo extends App {
  def myMethod[T <: MyClass](): Unit = {
      val t = new T // gives error: class type required by T found 
  }
  myMethod[MySubclassA]()
}


abstract class MyClass
class MySubclassA extends MyClass
class MySubclassB extends MyClass

回答1:

It fails to work, but not (primarily) because of type erasure, but because your definition should make sense for all T which satisfy the type bounds, and new T doesn't. E.g. T can be MyClass or an abstract subclass, or a subclass without a parameter-less constructor, or a trait (traits can extend classes), or...

If a runtime error is good enough, you can go with Sergey Lagutin's solution. But more reasonable for most cases would be to pass some way to create a T to myMethod. Possibly as an implicit argument, e.g.

class Factory[T](f: () => T) {
  def make(): T = f()
}
object Factory {
  implicit val MySubclassAFactory = 
    new Factory(() => new MySubclassA) 
  implicit val MySubclassBFactory = 
    new Factory(() => new MySubclassB)
}
def myMethod[T <: MyClass](implicit factory: Factory[T]): Unit = {
  val t = factory.make()
  ...
}

myMethod[MySubclassA] // works
myMethod[MyClass] // compilation error
myMethod[ASubclassForWhichFactoryIsNotDefined] // compilation error


回答2:

You may use ClassTag to achieve your goal:

def myMethod[T <: MyClass : ClassTag]: T =
  implicitly[ClassTag[T]].runtimeClass.newInstance().asInstanceOf[T]

println(myMethod[MySubclassA]) // MySubclassA@66d2e7d9
println(myMethod[MySubclassB]) // MySubclassB@1efbd816