Passing a type parameter for instantiation

2020-04-21 04:15发布

问题:

Why wouldn't the scala compiler dig this:

class Clazz

class Foo[C <: Clazz] {
  val foo = new C  
}

class type required but C found
[error]   val a = new C
[error]               ^

Related question - How to get rid of : class type required but T found

回答1:

This is a classic generic problem that also happens in Java - you cannot create an instance of a generic type variable. What you can do in Scala to fix this, however, is to introduce a type evidence to your type parameter that captures the runtime type:

class Foo[C <: Clazz](implicit ct: ClassTag[C]) {
    val foo = ct.runtimeClass.newInstance
}

Note that this only works if the class has a constructor without any arguments. Since the parameter is implicit, you don't need to pass it when calling the Foo constructor:

Foo[Clazz]()


回答2:

I came up with this scheme, couldn't simplify it through a companion object thought.

class Clazz
class ClazzFactory {
  def apply = new Clazz
}

class Foo(factory: ClazzFactory) {
  val foo: Clazz = factory.apply
}

It's very annoying that ClazzFactory can't be an object rather than a class though. A simplified version:

class Clazz {
  def apply() = new Clazz
}

class Foo(factory: Clazz) {
  val foo: Clazz = factory.apply
}

This requires the caller to use the new keyword in order to provide the factory argument, which is already a minor enough annoyance relative to the initial problem. But, scala could have made this scenario all more elegant; I had to fallback here to passing a parameter of the type I wish to instantiate, plus the new keyword. Maybe there's a better way.

(motivation was to instantiate that type many times within the real Foo, that's why this is at all a solution; otherwise my pattern above is just redundantly meaningless).



标签: scala