Search code examples
scalatype-erasure

Type mismatch runtime error on ClassTag (Scala)


I am a C++ programmer and is trying to learn Scala. I want to achieve something similar to the following code using C++ template

template<typename T>
class Foo {
public:
    T* bar;
    /////////////////Other Code Omitted//////////////////////////
};

Its counter-part in Scala will not compile due to type erasure

class Foo[E](){
    val bar = new E() //Will not compile 
}

I have been searching the whole night for a workaround, this seems to be one of them

package test
import scala.reflect._

object Type {
  def newInstance[T: ClassTag](init_args: AnyRef*): T = {
    classTag[T].runtimeClass.getConstructors.head.newInstance(init_args: _*).asInstanceOf[T]
  }
}

class Foo[T1:ClassTag](init_args: AnyRef*){
  val bar = Type.newInstance[T1](init_args)
}

class TestClass(val arg:String){
  val data = arg
}

However, when I try to instantiate one (val test = new Foo[Test]("test")) in the scala console, it gives the following error

java.lang.IllegalArgumentException: argument type mismatch
  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  at ParActor.Type$.newInstance(ParActor.scala:32)
  at ParActor.Foo.<init>(ParActor.scala:37)
  ... 35 elided

I am not exactly sure what causes the problem and how to fix this. Other work around is also welcomed.


Solution

  • You should turn

    Type.newInstance[T1](init_args)
    

    into

    Type.newInstance[T1](init_args: _*)
    

    What : _* does is turn a list or sequence into a varargs argument. A varargs parameter AnyRef* is actually an IndexedSeq[AnyRef], more specifically a WrappedArray[AnyRef]. So when you pass init_args as an argument to newInstance without telling the compiler to interpret it as a varargs argument you are actually passing in WrappedArray(WrappedArray("test")).