问题
I want to pass a scala file containing a case class so my application compiles this case class during run time and start using it.
The main reason why I am doing this is because I want to avoid rebuilding my code every time the case class changes. So would be better to pass it as a parameter (in case you are wondered, the operations with this case class are generic so it is not required any rework in the transformations)
I was using these post1, post2 and post3 as references. So far my application looks like this:
import scala.io.Source
import scala.reflect.runtime.universe
import scala.tools.reflect._
object TestCompile {
def main(args: Array[String]): Unit = {
val path = "C:\\myWorkspace\\entity\\TestClass.scala"
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
val src = Source.fromFile(path).mkString.stripMargin
val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
}
}
The file TestClass.scala is like this:
case class TestClass(
val value : String,
val timeStamp : Long,
val rowKey : String,
val columnFamily : String
)
But I am getting an exception in
val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
Exception:
Exception in thread "main" scala.tools.reflect.ToolBoxError: reflective compilation has failed: cannot initialize the compiler due to java.lang.VerifyError: scala/tools/reflect/ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.liftedTree1$1(ToolBoxFactory.scala:344) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.compiler$lzycompute(ToolBoxFactory.scala:330) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.compiler(ToolBoxFactory.scala:329) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.liftedTree2$1(ToolBoxFactory.scala:356) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:354) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.parse(ToolBoxFactory.scala:413) at TestCompile$.main(App.scala:17) at TestCompile.main(App.scala) Caused by: java.lang.VerifyError: scala/tools/reflect/ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.liftedTree1$1(ToolBoxFactory.scala:334)
Below is the dependency I am using, however I tried with other versions always getting the same error:
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-reflect</artifactId>
<version>2.11.6</version>
</dependency>
What am I doing wrong?
回答1:
I can't reproduce VerifyError
.
I have
java.lang.ClassCastException: scala.runtime.BoxedUnit cannot be cast to java.lang.Class
in the line val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
.
tb.compile(tb.parse(src))
has type () => Any
, so tb.compile(tb.parse(src))()
has type Any
.
https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/reflect/ToolBox.scala#L129
Remove .asInstanceOf[Class[_]]
.
Also see Causes of getting a java.lang.VerifyError
回答2:
I got some time to test this today. The main problem I think is that the last expected line scala.reflect.classTag[TestClass].runtimeClass
is missing in TestClass.scala.
One other important thing to remember is that scala-compiler.jar
should also be in your class path.
Once I resolved that, I ran into another problem with Java/Scala object conversion. You have two options, make the timeStamp
field, either a String
or Java.lang.Long
. Since the String conversion is trivial, I've given below example for Java.lang.Long
.
With the above changes, my TestClass.scala looks like below:
case class TestClass(
value : String,
timeStamp : java.lang.Long,
rowKey : String,
columnFamily : String
) {}
scala.reflect.classTag[TestClass].runtimeClass
I copied it to /tmp/
directory for testing.
Testing
My TestCompile.scala looks like below:
import scala.io.Source
import scala.reflect.runtime.universe
import scala.tools.reflect._
object TestCompile {
def main(args: Array[String]): Unit = {
val path = "/tmp/TestClass.scala"
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
val src = Source.fromFile(path).mkString.stripMargin
println(src)
val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
val ctor = clazz.getDeclaredConstructors()(0)
val instance = ctor.newInstance("My value", new java.lang.Long(1234567890L), "Row1", "Column1")
println(instance.toString)
}
}
Output
TestClass(My value,1234567890,Row1,Column1)
来源:https://stackoverflow.com/questions/59661412/scala-tools-reflect-toolboxerror-reflective-compilation-has-failed-cannot-init