我有一个案例类,包括大约 20 个字段,它们都是原始类型。
case class A( f1: String, f2: Int .....)
我必须从命令行解析这些字段(不幸的是)。
我可以,但我真的不想写这 20 次
opt[String]("f1") required() valueName "<f1>" action { (x, c) =>
c.copy(f1 = x)
} text "f1 is required"
//...repeat 20 times
我可以通过反射获取字段名称和文件类型,但我不知道如何将这些信息粘贴到 for 循环中的此调用中
我可以将它与 shapeless 联系起来,但我仍然不熟悉,并且可以在没有 shapeless 的情况下完成吗?
==
scala 选项解析器 => scopt
最佳答案
这是一个仅使用运行时反射实现的版本。虽然它不如基于宏的解决方案优雅,但它只需要 scala-reflect.jar:
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
代码:
import scala.collection.mutable
import scala.reflect.runtime.universe._
def genericParser[T: TypeTag](programName: String): OptionParser[T] = new OptionParser[T](programName) {
val StringTpe: Type = typeOf[String]
val fields: List[MethodSymbol] = typeOf[T].decls.sorted.collect {
case m: MethodSymbol if m.isCaseAccessor ⇒ m
}
val values = mutable.Map.empty[TermName, Any]
/**
* Returns an instance of a [[scopt.Read]] corresponding to the provided type
*/
def typeToRead(tpe: Type): Read[Any] = (tpe match {
case definitions.IntTpe ⇒ implicitly[Read[Int]]
case StringTpe ⇒ implicitly[Read[String]]
// Add more types if necessary...
}) map identity[Any]
for (f ← fields) {
// kind of dynamic implicit resolution
implicit val read: Read[Any] = typeToRead(f.returnType)
opt[Any](f.name.toString) required() valueName s"<${f.name}>" foreach { value ⇒
values(f.name) = value
} text s"${f.name} is required"
}
override def parse(args: Seq[String], init: T): Option[T] = {
super.parse(args, init) map { _ ⇒
val classMirror = typeTag[T].mirror.reflectClass(typeOf[T].typeSymbol.asClass)
val constructor = typeOf[T].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror = classMirror.reflectConstructor(constructor)
val constructorArgs = constructor.paramLists.flatten.map(symbol ⇒ values(symbol.asTerm.name))
constructorMirror(constructorArgs: _*).asInstanceOf[T]
}
}
}
示例用法:
case class A(f1: String, f2: Int)
println(genericParser[A]("main").parse(args, A("", -1)))
需要考虑的几点:
copy
方法)。 parse
中传递的初始值根本不使用方法(但这无关紧要,因为所有参数都是必需的)。 String
和 Int
(如有必要,请参阅添加更多类型...评论)。 关于scala - 如何使用 scala 选项解析器解析通用案例类字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45630693/