0

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

准备表格:

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   
}

有更好的方法吗?有没有实现它的库?

4

2 回答 2

1

这种反射方法采用一个案例类并调用其配套应用,如果该字段不在数据中,则调用 getField 并获取默认参数。

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]()
于 2012-10-15T10:10:54.753 回答
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"
}
于 2012-10-15T14:04:56.360 回答