如何获得班级成员的名字?

时间:2012-10-13 21:08:34

标签: scala reflection javassist

我希望能够做到这样的事情:

准备表格:

val formDescription = formBuilder(_.textField[User](_.firstName)
             .textField[User](_.lastName)
             ).build

showForm(formDescription)

使用用户:

从用户填写的表单中提取数据
//contains data of a form submitted by a user:
val formData: Map[String, String] = getFormData 

val newUser  = User(id = randomUuid, firstName = formData.extract[User](_.firstName))

我看到的一个解决方案是使用动态代理来扩展提供的类并记住在他上面调用的内容:

def getFieldName[T:Manifest](foo: T => Any) = {
  val clazz = implicitly[Manifest[T]].erasure
  val proxy = createDynamicProxy(clazz)
  foo(proxy)
  proxy.lastInvokedMethodName   
}

有更好的方法吗?是否有任何lib已经实现了它?

2 个答案:

答案 0 :(得分:1)

这种反射方法采用案例类并调用其伴随应用,如果字段不在数据中,则调用getField并获取默认args。

import scala.reflect.runtime.{currentMirror => cm, universe => uni}
import uni._

def fromXML(xml: Node): Option[PluginDescription] = {
  def extract[A]()(implicit tt: TypeTag[A]): Option[A] = {
    // extract one field
    def getField(field: String): Option[String] = {
      val text = (xml \\ field).text.trim
      if (text == "") None else Some(text)
    }

    val apply = uni.newTermName("apply")
    val module = uni.typeOf[A].typeSymbol.companionSymbol.asModule
    val ts = module.moduleClass.typeSignature
    val m = (ts member apply).asMethod
    val im = cm reflect (cm reflectModule module).instance
    val mm = im reflectMethod m

    def getDefault(i: Int): Option[Any] = {
      val n = uni.newTermName("apply$default$" + (i+1))
      val m = ts member n
      if (m == NoSymbol) None
      else Some((im reflectMethod m.asMethod)())
    }
    def extractArgs(pss: List[List[Symbol]]): List[Option[Any]] =
      pss.flatten.zipWithIndex map (p => getField(p._1.name.encoded) orElse getDefault(p._2))
    val args = extractArgs(m.paramss)
    if (args exists (!_.isDefined)) None
    else Some(mm(args.flatten: _*).asInstanceOf[A])
  }
  // check the top-level tag
  xml match {
    case <plugin>{_*}</plugin>  => extract[PluginDescription]()
    case _                      => None
  }
}

这个想法是做类似的事情:

case class User(id: Int = randomUuid, firstName: String, lastName: String)

val user = extract[User]()

答案 1 :(得分:0)

这是我自己的解决方案:

    package utils

    import javassist.util.proxy.{MethodHandler, MethodFilter, ProxyFactory}
    import org.specs2.mutable._


    import javassist.util.proxy.Proxy

    import java.lang.reflect.{Constructor, Method}

    class DynamicProxyTest extends Specification with MemberNameGetter {

        "Dynamic proxy" should {
            "extract field name" in {
                memberName[TestClass](_.a) must ===("a")
                memberName[TestClass](_.i) must ===("i")
                memberName[TestClass](_.b) must ===("b")
                memberName[TestClass](_.variable) must ===("variable")
                memberName[TestClass](_.value) must ===("value")
                memberName[TestClass](_.method) must ===("method")  
            }

        }
    }

    trait MemberNameGetter {
        def memberName[T: Manifest](foo: T => Any) = {
            val mf = manifest[T]
            val clazz = mf.erasure
            val proxyFactory = new ProxyFactory
            proxyFactory.setSuperclass(clazz)
            proxyFactory.setFilter(new MethodFilter {
                def isHandled(p1: Method) = true
            })
            val newClass = proxyFactory.createClass()
            var lastInvokedMethod: String = null
            val mh = new MethodHandler {
                def invoke(p1: Any, p2: Method, p3: Method, p4: Array[AnyRef]) = {
                    lastInvokedMethod = p2.getName
                    p3.invoke(p1, p4: _*)
                }
            }
            val constructor = defaultConstructor(newClass)
            val parameters = defaultConstructorParameters(constructor)
            // val proxy = constructor.newInstance("dsf", new Integer(0))
            val proxy2 = constructor.newInstance(parameters: _*)
            proxy2.asInstanceOf[Proxy].setHandler(mh)
            foo(proxy2.asInstanceOf[T])
            lastInvokedMethod
        }

        private def defaultConstructor(c: Class[_])  =   c.getConstructors.head
        private def defaultConstructorParameters(constructor: Constructor[_]) = {
            val parameterTypes = constructor.getParameterTypes
            parameterTypes.map{
                case Integer.TYPE => Integer.valueOf(0)                  
                case _ => null
            }

        }
    }


    case class TestClass(a: String, i: Int, b: Boolean) {
        var variable = "asdf"
        val value = "asdfasdfasd"

        def method = "method"
    }

    val mh = new MethodHandler {
        def invoke(p1: Any, p2: Method, p3: Method, p4: Array[AnyRef]) = {
            lastInvokedMethod = p2.getName
            p3.invoke(p1, p4: _*)
        }
    }
    val constructor = defaultConstructor(newClass)
    val parameters = defaultConstructorParameters(constructor)
    // val proxy = constructor.newInstance("dsf", new Integer(0))
    val proxy2 = constructor.newInstance(parameters: _*)
    proxy2.asInstanceOf[Proxy].setHandler(mh)
    foo(proxy2.asInstanceOf[T])
    lastInvokedMethod
}

private def defaultConstructor(c: Class[_])  =   c.getConstructors.head
private def defaultConstructorParameters(constructor: Constructor[_]) = {
    val parameterTypes = constructor.getParameterTypes
    parameterTypes.map{
        case Integer.TYPE => Integer.valueOf(0)
        case java.lang.Double.TYPE => java.lang.Double.valueOf(0)
        case java.lang.Long.TYPE => java.lang.Long.valueOf(0)
        case java.lang.Boolean.TYPE => java.lang.Boolean.FALSE
        case _ => null
    }

}
}


case class TestClass(a: String, i: Int, b: Boolean) {
   var variable = "asdf"
   val value = "asdfasdfasd"

   def method = "method"
}