Scala宏可以';找不到java.util.List、java.lang.Object
更新:有关此问题的解决方案,请参阅下面的答案。还有第二个问题(宏现在找不到Pojo),关于第二个问题的问题如下: 我正在创建一个scala宏来自动从POJO生成案例类(以便更好地使用avro) 除了编译器阻塞了java.util.List和java.lang.Object等内置java类之外,其他一切都“正常” 我的问题是:如何在宏中生成代码,以便编译器解析java类 错误消息示例: (在Pojo.java中没有//coments)Scala宏可以';找不到java.util.List、java.lang.Object,java,scala,macros,scala-macros,scala-macro-paradise,Java,Scala,Macros,Scala Macros,Scala Macro Paradise,更新:有关此问题的解决方案,请参阅下面的答案。还有第二个问题(宏现在找不到Pojo),关于第二个问题的问题如下: 我正在创建一个scala宏来自动从POJO生成案例类(以便更好地使用avro) 除了编译器阻塞了java.util.List和java.lang.Object等内置java类之外,其他一切都“正常” 我的问题是:如何在宏中生成代码,以便编译器解析java类 错误消息示例: (在Pojo.java中没有//coments) problemdemo/avroschemas/src/mai
problemdemo/avroschemas/src/main/java/com/squarefoot/Pojo.java
:
problemdemo/macros/src/main/scala/com/squarefoot/converters/Caseify.scala
:
Sbt文件:
problemdemo/build.sbt
problemdemo/macros/build.sbt
problemdemo/avroschemas/build.sbt
基本上,您需要的不是
TypeName(“java.util.List”)
,而是类似于(基于中的示例,目前无法测试)Select(选择(这个(TypeName(“java”))、TypeName(“util”)、TypeName(“List”)
。如果在输入树上显示showRaw
,您应该可以更准确地看到。因此,不要使用TypeName(…toString)
,而是在上拆分。可能只是删除TypeName
:
val fieldType = f.typeSignature.typeConstructor
val typeArgs = f.typeSignature.typeArgs
就足够了吗?所以,我还没有一个可以工作的宏,但在Alexey Romanov的回答的帮助下,我已经解决了这个问题。此代码导致以下错误:
[error] /Users/marcin/development/repos/problemdemo/src/main/scala/com/squarefoot/converters/problemdemo.scala:10: not found: type com.squarefoot.Pojo
[error] @Caseify(classOf[com.squarefoot.Pojo])
关于这个问题,我将提出一个单独的问题
package com.squarefoot.converters
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.reflect.macros.Context
/**
* Generate case class from POJO
* ex:
* @Caseify(classOf[com.squarefoot.incominglisting])
* case class Incominglisting()
* NOTE that the type parameter to classOf must be provided as a fully
* qualified name, otherwise the macro code here won't be able to find it.
*
* Generates a case class with the same members as the public, non-static
* members of the pojo
*
* Note that you must have all types used in the POJO in scope where the macro
* is invoked
*/
class Caseify[T](source: Class[T]) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro CaseifyMacro.expand_impl[T]
}
object CaseifyMacro {
/** generate case class from POJO */
def expand_impl[T](c: Context)(annottees: c.Expr[Any]*) = {
import c.universe._
// macro expand the macro expression itself to extract param
val source: Class[T] = c.prefix.tree match {
case q"new Caseify($param)" => c.eval[Class[T]](c.Expr(param))
}
val rm = scala.reflect.runtime.currentMirror
val vars =
rm.classSymbol(source).toType.members.map(_.asTerm).
filter(_.isVar).filter(_.isPublic)
val fields = vars.map({f=>
val fieldName = TermName(f.name.toString)
val fieldType = tq"${f.typeSignature.typeConstructor.typeSymbol.fullName}"
val rawTypeArgs = f.typeSignature.typeArgs.map(a=>TypeName(a.toString))
val typeArgs = tq"${rawTypeArgs}"
println("typeArgs: "+typeArgs.toString)
println("fieldType:"+fieldType.getClass.toString+"|"+fieldType.toString)
println(f.typeSignature.typeSymbol.asType.name.getClass.toString)
val arraylistname = tq"java.util.ArrayList"
println("DEBUG:"+tq"${arraylistname}".toString+"|"+f.typeSignature.typeConstructor.typeSymbol.fullName)
q"val $fieldName: $fieldType"
if(rawTypeArgs.nonEmpty) {
val appliedFieldType = tq"${arraylistname}[..$rawTypeArgs]"
q"val $fieldName: $appliedFieldType"
}
else
q"val $fieldName: $fieldType"
})
annottees.map(_.tree) match {
case List(q"case class $newname()") => {
val q = c.Expr[Any](
// Add your own logic here, possibly using arguments on the annotation.
q"""
case class $newname(..$fields)
""")
println(q.toString)
q
}
// Add validation and error handling here.
}
}
}
非常感谢。我现在已经能够测试这个了。您给出的代码段应该可以工作,因为类型看起来与TypeName
生成的类型相同,但出于某种原因,编译器抱怨要取消它们。使用tq”“
我相信已经生成了您建议的结构。我将把我得到的作为另一个答案。不幸的是,由于另一个相关问题,宏无法工作(答案中会有详细信息,我将编辑问题以参考其他问题)。
package com.squarefoot;
public class Pojo {
//public java.util.List<Integer> foo;
public int bar;
//public java.util.List<Pojo> baz;
public java.lang.Object qux;
}
package com.squarefoot.converters
import com.squarefoot.Pojo
class Foomin {
val foobar: java.util.List[Int]
}
@Caseify(classOf[com.squarefoot.Pojo])
case class Demo()
package com.squarefoot.converters
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.reflect.macros.Context
/**
* Generate case class from POJO
* ex:
* @Caseify(classOf[com.squarefoot.incominglisting])
* case class Incominglisting()
* NOTE that the type parameter to classOf must be provided as a fully
* qualified name, otherwise the macro code here won't be able to find it.
*
* Generates a case class with the same members as the public, non-static
* members of the pojo
*
* Note that you must have all types used in the POJO in scope where the macro
* is invoked
*/
class Caseify[T](source: Class[T]) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro CaseifyMacro.expand_impl[T]
}
object CaseifyMacro {
/** generate case class from POJO */
def expand_impl[T](c: Context)(annottees: c.Expr[Any]*) = {
import c.universe._
// macro expand the macro expression itself to extract param
val source: Class[T] = c.prefix.tree match {
case q"new Caseify($param)" => c.eval[Class[T]](c.Expr(param))
}
val rm = scala.reflect.runtime.currentMirror
val vars =
rm.classSymbol(source).toType.members.map(_.asTerm).
filter(_.isVar).filter(_.isPublic)
lazy val fields = vars.map({f=>
val fieldName = TermName(f.name.toString)
val fieldType = TypeName(f.typeSignature.typeConstructor.toString)
val typeArgs = f.typeSignature.typeArgs.map(a=>TypeName(a.toString))
println("fieldType:"+fieldType.toString)
q"val $fieldName: $fieldType"
if(typeArgs.size > 0)
q"val $fieldName: $fieldType[..$typeArgs]"
else
q"val $fieldName: $fieldType"
})
annottees.map(_.tree) match {
case List(q"case class $newname()") => {
val q = c.Expr[Any](
// Add your own logic here, possibly using arguments on the annotation.
q"""
case class $newname(..$fields)
""")
println(q.toString)
q
}
// Add validation and error handling here.
}
}
}
name := "data-importer"
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
scalaVersion := "2.11.8"
val avroVersion = "1.8.1"
lazy val root =
project.in( file(".") )
.aggregate(avroschemas, macros).dependsOn(macros, avroschemas)
lazy val macros = project.dependsOn(avroschemas)
lazy val avroschemas = project
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value
)
// better error reporting
scalacOptions in Test ++= Seq("-Yrangepos")
run in Compile := Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run))
name := "data-importer-macros"
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
organization := "com.squarefoot"
scalaVersion := "2.11.3"
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value
)
scalacOptions in Test ++= Seq("-Yrangepos")
name := "data-importer-avroschemas"
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
organization := "com.squarefoot"
scalaVersion := "2.11.8"
// better error reporting
scalacOptions in Test ++= Seq("-Yrangepos")
run in Compile := Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run))
val fieldType = f.typeSignature.typeConstructor
val typeArgs = f.typeSignature.typeArgs
[error] /Users/marcin/development/repos/problemdemo/src/main/scala/com/squarefoot/converters/problemdemo.scala:10: not found: type com.squarefoot.Pojo
[error] @Caseify(classOf[com.squarefoot.Pojo])
package com.squarefoot.converters
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.reflect.macros.Context
/**
* Generate case class from POJO
* ex:
* @Caseify(classOf[com.squarefoot.incominglisting])
* case class Incominglisting()
* NOTE that the type parameter to classOf must be provided as a fully
* qualified name, otherwise the macro code here won't be able to find it.
*
* Generates a case class with the same members as the public, non-static
* members of the pojo
*
* Note that you must have all types used in the POJO in scope where the macro
* is invoked
*/
class Caseify[T](source: Class[T]) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro CaseifyMacro.expand_impl[T]
}
object CaseifyMacro {
/** generate case class from POJO */
def expand_impl[T](c: Context)(annottees: c.Expr[Any]*) = {
import c.universe._
// macro expand the macro expression itself to extract param
val source: Class[T] = c.prefix.tree match {
case q"new Caseify($param)" => c.eval[Class[T]](c.Expr(param))
}
val rm = scala.reflect.runtime.currentMirror
val vars =
rm.classSymbol(source).toType.members.map(_.asTerm).
filter(_.isVar).filter(_.isPublic)
val fields = vars.map({f=>
val fieldName = TermName(f.name.toString)
val fieldType = tq"${f.typeSignature.typeConstructor.typeSymbol.fullName}"
val rawTypeArgs = f.typeSignature.typeArgs.map(a=>TypeName(a.toString))
val typeArgs = tq"${rawTypeArgs}"
println("typeArgs: "+typeArgs.toString)
println("fieldType:"+fieldType.getClass.toString+"|"+fieldType.toString)
println(f.typeSignature.typeSymbol.asType.name.getClass.toString)
val arraylistname = tq"java.util.ArrayList"
println("DEBUG:"+tq"${arraylistname}".toString+"|"+f.typeSignature.typeConstructor.typeSymbol.fullName)
q"val $fieldName: $fieldType"
if(rawTypeArgs.nonEmpty) {
val appliedFieldType = tq"${arraylistname}[..$rawTypeArgs]"
q"val $fieldName: $appliedFieldType"
}
else
q"val $fieldName: $fieldType"
})
annottees.map(_.tree) match {
case List(q"case class $newname()") => {
val q = c.Expr[Any](
// Add your own logic here, possibly using arguments on the annotation.
q"""
case class $newname(..$fields)
""")
println(q.toString)
q
}
// Add validation and error handling here.
}
}
}