Scala Reflection& TypeTag Mismatch

时间:2016-01-22 19:44:57

标签: scala reflection avro

所以我在.jar中有一堆编译的case类。我想加载并迭代所有这些,并为每个案例类使用scalavro生成Avro架构。 scalavro AvroType需要一个TypeTag,所以基本上我的问题是如何从jar中的外部case类中适当地反映TypeTags。

import java.net.URL
import com.gensler.scalavro.types.AvroType
import org.clapper.classutil.ClassFinder
import java.io.File
import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{ universe => ru }

object scalaAvroGen extends App {
  val jarLocation = "someCaseClasses.jar"
  val classpath = List(jarLocation).map(new File(_))
  val classLoader = new URLClassLoader(Array[URL](new File(jarLocation).toURI.toURL), this.getClass().getClassLoader())
  val finder = ClassFinder(classpath)
  val classes = finder.getClasses.filterNot(_.isFinal)
  classes.foreach {
    loadedClass => {
      val typeTag = typeToTypeTag(getType(classLoader.loadClass(loadedClass.name)))
      val avroSchema = AvroType.apply(typeTag).schema()
      println(avroSchema)
    }
  }

  def getType[T](clazz: Class[T])(implicit runtimeMirror: ru.Mirror) =
    runtimeMirror.classSymbol(clazz).toType

  def typeToTypeTag[T](tpe: Type): TypeTag[T] = TypeTag.synchronized {
    val mirror = scala.reflect.runtime.currentMirror
    TypeTag(mirror, new reflect.api.TypeCreator {
      def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
        assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
        tpe.asInstanceOf[U#Type]
      }
    })
  }
}

编译的.jar中的示例case类:

case class SimpleScalaAvroObject(version: Int, name: String)

目前,当我尝试运行此代码时,出现以下错误:

Error:(20, 39) type mismatch;  found   : reflect.runtime.universe.TypeTag[Nothing]  required: reflect.runtime.universe.TypeTag[T] Note: Nothing <: T, but trait TypeTag is invariant in type T. You may wish to investigate a wildcard type such as `_ <: T`. (SLS 3.2.10)
      val avroSchema = AvroType.apply(typeTag).schema()    
                              ^

现在我确定这不是我唯一的问题,我花了最后两天把所有东西扔到这里。有一次我在SimpleScalaAvroObject上得到了类未找到的异常,所以我可能甚至没有正确反映。

我最终会对我真正关心的案例类进行注释,但现在这只是一个PoC。

请注意,直到几个月前,我还是一个C#dev,对于Scala乱七八糟的混乱感到抱歉。

谢谢!

1 个答案:

答案 0 :(得分:2)

您可以使用另一个项目avro4s(免责声明:我写了这个),在编译时而不是运行时生成模式,并且您应该消除任何类型标记问题(因为编译器可以访问更多)有关运行时存在的信息。

使用非常简单,

case class SimpleScalaAvroObject(version: Int, name: String)
// this schema is of type org.apache.avro.Schema
val schema = AvroSchema[SimpleScalaAvroObject]