您可以使用無形此:
case class UserDetails(username: String, contact: Contact)
case class Contact(phone: String, email: Email)
case class Email(name: String, domain: String)
// a class to store field name and subfields info
case class FieldInfo(name: String, subfields: List[FieldInfo])
// our main typeclass
// we will use shapeless to derive instance for UserDetails above
trait FieldList[A] {
def fields: List[FieldInfo]
}
object FieldList {
import shapeless.{ LabelledGeneric, HList, ::, HNil, Witness, Lazy }
import shapeless.labelled.{ FieldType, field }
// helper function for creating typeclass instance
def createInstance[A](f: => List[FieldInfo]): FieldList[A] = new FieldList[A] {
def fields = f
}
// instance for String, here we just care for field name so just a dummy instance...
// you should add instance for other basic types (Int, Double...) if required
implicit val stringInstance: FieldList[String] = createInstance(Nil)
// define instance for base case (HNil)
implicit val hnilInstance: FieldList[HNil] = createInstance(Nil)
// define rule for HList
implicit def hlistInstance[K <: Symbol, H, T <: HList](
implicit witness: Witness.Aux[K],
hInstance: Lazy[FieldList[H]],
tInstance: FieldList[T]): FieldList[FieldType[K, H] :: T] = createInstance {
val headFieldName: String = witness.value.name
val headSubFields = hInstance.value.fields
val tail = tInstance.fields
FieldInfo(headFieldName, headSubFields) :: tail
}
// rule for generic ADT
implicit def genericObjectInstance[A, H <: HList](
implicit generic: LabelledGeneric.Aux[A, H],
hlistInstance: Lazy[FieldList[H]]): FieldList[A] = createInstance { hlistInstance.value.fields }
// some utils...
def show(field: FieldInfo): List[String] = {
if (field.subfields.isEmpty) List(field.name)
else field.subfields.flatMap(sub => show(sub).map(field.name + "." + _))
}
def getFieldsName[A](implicit instance: FieldList[A]) = {
val fields = instance.fields
fields.flatMap(show).mkString("\n")
}
}
// ok, test it!
object ShapelessLabelled extends App {
println(FieldList.getFieldsName[UserDetails])
}
爲了更好地理解這一點,你可以看看偉大嚮導在https://github.com/underscoreio/shapeless-guide
於無形隱式的宏和無形的類型類可以很容易的工作。使用'shapeless.LabelledGeneric'編碼爲HList,使用標籤中的'Keys'來獲得你想要的。 – flavian