【问题标题】:Scala macro can't find java.util.List, java.lang.ObjectScala 宏找不到 java.util.List、java.lang.Object
【发布时间】:2016-12-11 17:20:29
【问题描述】:

更新:请参阅下面的答案以了解 this 问题的解决方案。还有第二个问题(宏现在找不到Pojo),关于第二个问题的问题在这里:Scala macro can't find my java class

我正在创建一个 scala 宏来自动从 POJO 生成案例类(以便更好地使用 avro)。

除了编译器在 java.util.List 和 java.lang.Object 等内置 java 类上阻塞之外,一切都“有效”。

我的问题是:如何在宏中生成代码以便编译器解析 java 类?

错误信息示例:

(在 Pojo.java 中没有 // 注释)

[info] Compiling 1 Scala source to /Users/marcin/development/repo/problemdemo/target/scala-2.11/classes...
fieldType:java.util.List
fieldType:Int
fieldType:java.util.List
Expr[Any](case class Demo extends scala.Product with scala.Serializable {
  <caseaccessor> <paramaccessor> val baz: java.util.List[com.squarefoot.Pojo] = _;
  <caseaccessor> <paramaccessor> val bar: Int = _;
  <caseaccessor> <paramaccessor> val foo: java.util.List[java.lang.Integer] = _;
  def <init>(baz: java.util.List[com.squarefoot.Pojo], bar: Int, foo: java.util.List[java.lang.Integer]) = {
    super.<init>();
    ()
  }
})
[error] /Users/marcin/development/repos/problemdemo/src/main/scala/com/squarefoot/converters/problemdemo.scala:5: not found: type java.util.List
[error] @Caseify(classOf[com.squarefoot.Pojo])
[error]  ^
[error] one error found
[error] (root/compile:compileIncremental) Compilation failed
[error] Total time: 17 s, completed Dec 11, 2016 12:00:57 PM

(Pojo.java如图)

[info] Compiling 1 Scala source to /Users/marcin/development/repos/problemdemo/target/scala-2.11/classes...
fieldType:java.lang.Object
fieldType:Int
Expr[Any](case class Demo extends scala.Product with scala.Serializable {
  <caseaccessor> <paramaccessor> val qux: java.lang.Object = _;
  <caseaccessor> <paramaccessor> val bar: Int = _;
  def <init>(qux: java.lang.Object, bar: Int) = {
    super.<init>();
    ()
  }
})
[error] /Users/marcin/development/repos/problemdemo/src/main/scala/com/squarefoot/converters/problemdemo.scala:5: not found: type java.lang.Object
[error] @Caseify(classOf[com.squarefoot.Pojo])
[error]  ^
[error] one error found
[error] (root/compile:compileIncremental) Compilation failed
[error] Total time: 6 s, completed Dec 11, 2016 12:04:29 PM

编辑:showRaw 的结果

showRaw 给出这样的输出,我觉得这很好:

ValDef(Modifiers(DEFERRED), TermName("availablebuildouts"), AppliedTypeTree(Ident(TypeName("java.util.List")), List(Ident(TypeName("com.squarefoot.buildouttype")))), EmptyTree)

problemdemo/avroschemas/src/main/java/com/squarefoot/Pojo.java:

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;
}

problemdemo/src/main/scala/com/squarefoot/converters/problemdemo.scala:

package com.squarefoot.converters

import com.squarefoot.Pojo

class Foomin {
  val foobar: java.util.List[Int]
}
    
@Caseify(classOf[com.squarefoot.Pojo])
case class Demo()

problemdemo/macros/src/main/scala/com/squarefoot/converters/Caseify.scala:

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.
    }
  }
}

Sbt 文件:

problemdemo/build.sbt

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))

problemdemo/macros/build.sbt

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")

problemdemo/avroschemas/build.sbt

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))

【问题讨论】:

    标签: java scala macros scala-macros scala-macro-paradise


    【解决方案1】:

    基本上,您想要的不是TypeName("java.util.List"),而是类似(基于http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#tree-creation-via-reify 中的示例,目前无法测试)Select(Select(This(TypeName("java")), TypeName("util")), TypeName("List"))。如果你在你的 input 树上做showRaw,你应该看得更准确。所以不是TypeName(...toString),而是.。也许只是删除TypeName

    val fieldType = f.typeSignature.typeConstructor
    val typeArgs = f.typeSignature.typeArgs
    

    够了吗?

    【讨论】:

    • 非常感谢您。我现在已经能够对此进行测试了。你给的 sn-p should 工作,因为类型看起来与TypeName 生成的类型相同,但由于某种原因编译器抱怨解除它们。使用tq"" - 我相信 - 生成了您建议的结构。我将发布我所拥有的作为另一个答案。不幸的是,由于另一个相关问题,宏无法正常工作(详细信息将在答案中提供,我将编辑问题以引用其他问题)。
    【解决方案2】:

    所以,我还没有一个可以工作的宏,但是在 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.
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-06
      • 1970-01-01
      • 2013-08-05
      • 2018-04-03
      相关资源
      最近更新 更多