基于Scala类型的属性提取器 – 仅限Getter镜头?
从数据容器(例如案例类)中提取类型的最佳方法是什么.
例如,如果我有类型标签[U] = {type Tag = U}标记类型特征PID,它是标记的Int类型ProductId =具有标记[PID]的Int或scalaz样式类型ProductId = Int @@ PID并说出其他字段在产品类型Name = String @@ PNameetc和一个包含产品属性的数据容器中; case class Product(pid: ProductId,name: Name,weight: Weight) 我怎么能写一个通用的提取器A => B风格方法无需借助反射? 原因是我想在运行时从Product容器中动态提取字段.即用户传递他们想要提取的产品的属性. 即如果我想动态获取ProductId,我可以编写一个接受类型的方法并返回一个值,例如 trait Extractor[A] { def extract[B](i: A): B = //get type B from the Product class } 或者我是在复杂的事情. 我可以写一个简单的提取器类,它取A => B功能并为每种类型定义它; trait Getter[A,B] { def extract(i: A): B } //... mix this in... trait GetPID extends Getter[Product,ProductId] { override def extract(implicit i: Product) = i.pid } trait GetName extends Getter[Product,Name] { override def extract(implicit i: Product) = i.name } 然后在需要的地方添加它们. val dyn = new DynamicProductExtractor with GetPID dyn.extract 但这似乎很麻烦. 我觉得Lens在这里很有用. 解决方法
为了完整的例子,假设我们有以下类型和一些示例数据:
import shapeless._,tag._ trait PID; trait PName; trait PWeight type ProductId = Int @@ PID type Name = String @@ PName type Weight = Double @@ PWeight case class Product(pid: ProductId,weight: Weight) val pid = tag[PID](13) val name = tag[PName]("foo") val weight = tag[PWeight](100.0) val product = Product(pid,name,weight) 我在这里使用的是Shapeless的标签,但是下面的所有内容都与Scalaz或你自己的Tagged相同.现在假设我们希望能够在任意案例类中按类型查找成员,我们可以使用Shapeless的Generic创建一个提取器: import ops.hlist.Selector def extract[A] = new { def from[C,Repr <: HList](c: C)(implicit gen: Generic.Aux[C,Repr],sel: Selector[Repr,A] ) = sel(gen.to(c)) } 请注意,为了简单和简洁,我使用的是结构类型,但您可以非常轻松地定义一个可以执行相同操作的新类. 现在我们可以写下面的内容: scala> extract[ProductId].from(product) res0: Int with shapeless.tag.Tagged[PID] = 13 请注意,如果案例类具有多个具有所请求类型的成员,则将返回第一个成员.如果它没有任何具有正确类型的成员(例如from(product)from extract [Char]),那么你将得到一个很好的编译时错误. 你可以在这里使用镜头,但你需要写更多或更少相同的机器 – 我不知道一个镜头实现,它给你按类型索引(无形,例如提供位置索引和成员名称索引). (请注意,这不是真正的“动态”,因为您在case类中查找的类型必须在编译时静态知道,但在上面给出的示例中也是如此.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |