英文:
How to get field names and field types from a Generic Type in Scala?
问题
在Scala中,给定一个泛型类型T,如何检索字段名称和字段类型的列表?例如,如果我有以下案例类:
case class Person(name: String, age: Int, gender: Boolean)
和以下泛型函数:
def getFieldNamesAndTypes[T](): Seq[(String, String)]
我希望能够检索到字段(名称,类型)的序列(按字段出现的顺序):
val fieldNamesAndTypes = getFieldNamesAndTypes[Person]()
英文:
In Scala, given a generic type T, how to retrieve the list of field names and the field types? For example, if I have the case class:
case class Person(name: String, age: Int, gender: Boolean)
And the generic function:
def getFieldNamesAndTypes[T](): Seq[(String, String)]
I would like to be able to retrieve a sequence (in order that the fields appear) of the fields (name, type):
val fieldNamesAndTypes = getFieldNamesAndTypes[Person]()
答案1
得分: 4
看起来你需要使用反射 API:
欢迎来到 Scala 2.13.1(OpenJDK 64 位服务器 VM,Java 1.8.0_222)。
输入表达式进行评估。或尝试 :help。
import scala.reflect.runtime.universe._
def getFields[T: TypeTag] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => (m.name.toString, m.returnType.toString)
}
case class Person(name: String, age: Int, gender: Boolean)
getFields[Person]
输出结果如下:
res0: Iterable[(String, String)] = List((gender,Boolean), (age,Int), (name,String))
英文:
It seems that you need the reflection API:
Welcome to Scala 2.13.1 (OpenJDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> def getFields[T: TypeTag] = typeOf[T].members.collect {
| case m: MethodSymbol if m.isCaseAccessor => (m.name.toString, m.returnType.toString)
| }
getFields: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])Iterable[(String, String)]
scala> case class Person(name: String, age: Int, gender: Boolean)
defined class Person
scala> getFields[Person]
res0: Iterable[(String, String)] = List((gender,Boolean), (age,Int), (name,String))
答案2
得分: 4
你可以在编译时使用Shapeless来实现这个,即在底层使用编译时反射。
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
import shapeless.{HList, LabelledGeneric, Poly0, Poly1, Typeable, Witness}
object fieldTypePoly extends Poly1 {
implicit def cse[K <: Symbol, V](implicit
witness: Witness.Aux[K],
typeable: Typeable[V]
): Case.Aux[FieldType[K, V], (String, String)] =
at(_ => witness.value.name -> typeable.describe)
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def getFieldNamesAndTypes[T] = new PartiallyApplied[T]
class PartiallyApplied[T] {
def apply[L <: HList, L1 <: HList]()(implicit
generic: LabelledGeneric.Aux[T, L],
mapper: Mapper.Aux[fieldTypePoly.type, L, L1],
fillWith: FillWith[nullPoly.type, L],
toList: ToList[L1, (String, String)]
): Seq[(String, String)] = toList(mapper(fillWith()))
}
case class Person(name: String, age: Int, gender: Boolean)
getFieldNamesAndTypes[Person]() // List((name,String), (age,Int), (gender,Boolean))
英文:
You can do this even at compile time using Shapeless (i.e. using compile-time reflection under the hood)
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
import shapeless.{HList, LabelledGeneric, Poly0, Poly1, Typeable, Witness}
object fieldTypePoly extends Poly1 {
implicit def cse[K <: Symbol, V](implicit
witness: Witness.Aux[K],
typeable: Typeable[V]
): Case.Aux[FieldType[K, V], (String, String)] =
at(_ => witness.value.name -> typeable.describe)
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def getFieldNamesAndTypes[T] = new PartiallyApplied[T]
class PartiallyApplied[T] {
def apply[L <: HList, L1 <: HList]()(implicit
generic: LabelledGeneric.Aux[T, L],
mapper: Mapper.Aux[fieldTypePoly.type, L, L1],
fillWith: FillWith[nullPoly.type, L],
toList: ToList[L1, (String, String)]
): Seq[(String, String)] = toList(mapper(fillWith()))
}
case class Person(name: String, age: Int, gender: Boolean)
getFieldNamesAndTypes[Person]() // List((name,String), (age,Int), (gender,Boolean))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论