Ответ 1
Вы можете сделать это, проверив список, указанный тегом типа, и отразив его зеркало:
import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag
def printInfo[A](a: A)(implicit tt: TypeTag[A], ct: ClassTag[A]): String = {
val members = tt.tpe.members.collect {
case m if m.isMethod && m.asMethod.isCaseAccessor => m.asMethod
}
members.map { member =>
val memberValue = tt.mirror.reflect(a).reflectMethod(member)()
s"(${ member.name }, ${ member.returnType }, $memberValue)"
}.mkString(", ")
}
Что будет работать следующим образом:
scala> case class User(id: Long, name: String) {
| private var foo = "Foo" // shouldn't be printed
| val bar = "bar" // also shouldn't be printed
| }
defined class User
scala> case class Message(id: Long, userId: Long, text: String)
defined class Message
scala> printInfo(User(1, "usr1"))
res0: String = (name, String, usr1), (id, scala.Long, 1)
scala> printInfo(Message(1, 1, "Hello World"))
res1: String = (text, String, Hello World), (userId, scala.Long, 1), (id, scala.Long, 1)
(Если вы хотели Long
вместо scala.Long
, было бы не так сложно отбросить префикс от типа, который вы получаете от member.returnType
, но я оставлю это как упражнение для читателя.)
Также не слишком сложно сделать это без отражения во время выполнения с использованием Shapeless:
import shapeless.{ ::, HList, HNil, LabelledGeneric, Typeable, Witness }
import shapeless.labelled.FieldType
trait PrettyPrintable[A] {
def apply(a: A): List[(String, String, String)]
}
object PrettyPrintable {
implicit val hnilPrettyPrintable: PrettyPrintable[HNil] =
new PrettyPrintable[HNil] {
def apply(a: HNil): List[(String, String, String)] = Nil
}
implicit def hconsPrettyPrintable[K <: Symbol, H, T <: HList](implicit
kw: Witness.Aux[K],
ht: Typeable[H],
tp: PrettyPrintable[T]
): PrettyPrintable[FieldType[K, H] :: T] =
new PrettyPrintable[FieldType[K, H] :: T] {
def apply(a: FieldType[K, H] :: T): List[(String, String, String)] =
(kw.value.name, ht.describe, a.head.toString) :: tp(a.tail)
}
implicit def genPrettyPrintable[A, R <: HList](implicit
ag: LabelledGeneric.Aux[A, R],
rp: PrettyPrintable[R]
): PrettyPrintable[A] = new PrettyPrintable[A] {
def apply(a: A): List[(String, String, String)] = rp(ag.to(a))
}
def printInfo[A](a: A)(implicit pp: PrettyPrintable[A]) = pp(a).map {
case (memberName, memberType, memberValue) =>
s"($memberName, $memberType, $memberValue)"
}.mkString(", ")
}
И затем:
scala> PrettyPrintable.printInfo(User(1, "usr1"))
res2: String = (id, Long, 1), (name, String, usr1)
scala> PrettyPrintable.printInfo(Message(1, 1, "Hello World"))
res3: String = (id, Long, 1), (userId, Long, 1), (text, String, Hello World)
Кроме всего прочего, это дает вам поля в порядке декларации, которые, я думаю, должны быть возможны с помощью подхода типа тегов, но я избегаю этого API так часто, как я могу, поэтому с головы до головы я не уверен.