我试图编写一个宏,它接受带有java bean接口和case类的类,并创建一对用于在它们之间进行映射的方法。
我试图检查每个属性的类型是否匹配,但java bean中的类型是例如java.lang.Long和case类中的类型是scala.Long。
我的问题是,鉴于这些2的c.universe.Type对象,有没有办法测试它们之间是否存在隐式转换?即测试我是否可以将一个传递给期望另一个的方法。
答案 0 :(得分:0)
如果要检查是否存在隐式转换,可以使用c.inferImplicitView
。
概念证明:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
def test[T,S,R](value: T, func: S => R): R = macro Macro.myMacro[T,S,R]
object Macro {
def myMacro[T: c.WeakTypeTag,S: c.WeakTypeTag,R](c: Context)(value: c.Tree, func: c.Tree): c.Tree = {
import c.universe._
val view = c.inferImplicitView(value, weakTypeOf[T], weakTypeOf[S])
if (view == EmptyTree)
c.abort(c.enclosingPosition, "Cannot apply function")
else
q"$func($value)"
}
}
// Exiting paste mode, now interpreting.
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
defined term macro test: [T, S, R](value: T, func: S => R)R
defined object Macro
scala> test(3L, (l: java.lang.Long) => l.toString)
res20: String = 3
scala> test(3L, (l: java.lang.Integer) => l.toString)
<console>:23: error: Cannot apply function
test(3L, (l: java.lang.Integer) => l.toString)
^
如果您没有value
,显然如果您执行c.inferImplicitView(EmptyTree, weakTypeOf[T], weakTypeOf[S])
,它也会有效。
更接近实际问题的更复杂的例子:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
def mapBetween[JB,CC](jb: JB): CC = macro Macro.myMacro[JB,CC]
object Macro {
def myMacro[JB: c.WeakTypeTag, CC: c.WeakTypeTag](c: Context)(jb: c.Tree): c.Tree = {
import c.universe._
val jbTpe = weakTypeOf[JB]
val ccTpe = weakTypeOf[CC]
val constructor = ccTpe.members.filter(m =>
m.isConstructor && m.name != TermName("$init$")
).head.asMethod
if(constructor.paramLists.size != 1 || constructor.paramLists.head.size != 1)
c.abort(c.enclosingPosition, "not supported :(")
val ccParam = constructor.paramLists.head.head
val ccParamType = ccParam.typeSignature
val ccParamName = ccParam.name.toString
val jbGetter = jbTpe.member(TermName(s"get${ccParamName.head.toUpper + ccParamName.tail}"))
val getterType = jbGetter.asMethod.returnType
val view = c.inferImplicitView(EmptyTree, getterType, ccParamType)
if (view == EmptyTree)
c.abort(c.enclosingPosition, "Cannot apply function")
else
q"new ${ccTpe.typeSymbol.name.toTypeName}($jb.${jbGetter.name.toTermName})"
}
}
// Exiting paste mode, now interpreting.
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
defined term macro mapBetween: [JB, CC](jb: JB)CC
defined object Macro
scala> case class CaseClass(foo: Int)
defined class CaseClass
scala> class JavaBean{ def getFoo(): java.lang.Integer = 42 }
defined class JavaBean
scala> mapBetween[JavaBean,CaseClass](new JavaBean)
res0: CaseClass = CaseClass(42)
scala> case class CaseClass(foo: Int)
defined class CaseClass
scala> class JavaBean{ def getFoo(): java.lang.Double = 42.0 }
defined class JavaBean
scala> mapBetween[JavaBean,CaseClass](new JavaBean)
<console>:27: error: Cannot apply function
mapBetween[JavaBean,CaseClass](new JavaBean)
^