无形的Generic
提供案例类和密封特征的乘积和表示,这意味着如果我们有一个像这样的简单 ADT:
sealed trait Base
case object Foo extends Base
case class Bar(i: Int, s: String) extends Base
Then Generic[Base]
会给我们一个映射到Foo.type :+: Bar :+: CNil
—i.e. a Foo.type
or a Bar
(其中or意味着我们正在谈论类型理论术语中的“和类型”),以及Generic[Bar]
给我们一个映射到Int :: String :: HNil
,即Int
and a String
(产品类型,其中“产品”的含义与“产品”的含义大致相同)scala.ProductN
标准库中的类型)。
LabelledGeneric
使用乘积和表示的增强版本,其中乘积或总和中的每个项都标有标签。对于密封特征,这些将是每个子类型的构造函数名称,对于案例类,它们将是成员名称。这些不是完全限定的名称,只是在本地消除歧义的标签。
Generic
and LabelledGeneric
无意用作编译时反射的通用工具。例如,它们不可用于任意类型,并且不提供对类型本身名称的访问。
你最好的选择可能是使用TypeTag
,但是如果您想要名称的类型级别表示(例如LabelledGeneric
提供标签),您需要使用宏生成的实例定义自己的类型类。像下面这样的东西应该有效:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
trait TypeInfo[A] { type Name <: String; def name: String }
object TypeInfo {
type Aux[A, Name0 <: String] = TypeInfo[A] { type Name = Name0 }
def apply[A](implicit ti: TypeInfo[A]): Aux[A, ti.Name] = ti
implicit def materializeTypeInfo[A, Name <: String]: Aux[A, Name] =
macro matTypeInfoImpl[A, Name]
def matTypeInfoImpl[A: c.WeakTypeTag, Name <: String](c: Context): c.Tree = {
import c.universe._
val A = c.weakTypeOf[A]
val name = A.typeSymbol.name.decodedName.toString.trim
q"new TypeInfo[$A] { type Name = ${ Constant(name) }; def name = $name }"
}
}
但是,如果您只需要值级字符串,这对于您的用例来说可能有点过分了。