如何使用无形将字段从一个类复制到另一个类

时间:2017-03-07 23:07:55

标签: scala shapeless

是否可以使用无形来将1个对象转换为另一个对象

  • 进行一些小的转换,例如将Option [T]转换为T. (没有手动定义每个类的映射)

  • 忽略缺少的字段

导入shapeless._ import shapeless.syntax ._

case class Cat(color: Option[Int], isFat: Boolean, newField: String)
case class Kitten(color: Int, isFat: Boolean)

val kitten = Kitten(2, true)

val genCat = Generic[Cat]
val genKit = Generic[Kitten]

val cat: Cat = genCat.from(genKit.to(kitten))

此操作失败,出现以下错误 (扩展为)无形。:: [Int,shapeless。:: [Boolean,shapeless.HNil]]

1 个答案:

答案 0 :(得分:0)

以下是使用与previous answer相同的创意解决方案。

  • 进行次要转换(默认值选项)
  • 忽略尾部字段

当然有一些限制。

object HListsFlatten {

  import shapeless.{::, HList, HNil}

  sealed trait DefaultValue[V] {
    def value: V
  }

  implicit val defaultInt: DefaultValue[Int] = new DefaultValue[Int] {
    override def value = 0
  }

  sealed trait HConv[From <: HList, To <: HList] {
    def convert(list: From): To
  }

  implicit def buildHConvNil: HConv[HNil, HNil] = new HConv[HNil, HNil] {
    override def convert(list: HNil): HNil = HNil
  }

  implicit def buildHConvShorten[H <: AnyVal, T <: HList]
    : HConv[::[H, T], ::[H, HNil]] = new HConv[::[H, T], ::[H, HNil]] {
    override def convert(list: ::[H, T]): ::[H, HNil] = {
      list.head :: HNil
    }
  }

  implicit def buildHConvOption[H, T <: HList, T2 <: HList](
      implicit conv: HConv[T, T2],
      default: DefaultValue[H]): HConv[::[Option[H], T], ::[H, T2]] =
    new HConv[::[Option[H], T], ::[H, T2]] {
      override def convert(list: ::[Option[H], T]): ::[H, T2] = {
        list.head.getOrElse(default.value) :: conv.convert(list.tail)
      }
    }

  implicit def buildHConv[H <: AnyVal, T <: HList, T2 <: HList](
      implicit conv: HConv[T, T2]): HConv[::[H, T], ::[H, T2]] =
    new HConv[::[H, T], ::[H, T2]] {
      override def convert(list: ::[H, T]): ::[H, T2] = {
        list.head :: conv.convert(list.tail)
      }
    }

  implicit def buildHConvString[T <: HList, T2 <: HList](
      implicit conv: HConv[T, T2]): HConv[::[String, T], ::[String, T2]] =
    new HConv[::[String, T], ::[String, T2]] {
      override def convert(list: ::[String, T]): ::[String, T2] = {
        list.head :: conv.convert(list.tail)
      }
    }

  def flatten[A <: HList, B <: HList](list: A)(implicit conv: HConv[A, B]): B =
    conv.convert(list)

}

示例:

import shapeless.Generic

case class Cat(color: Option[Int], isFat: Boolean, newField: String)
case class Kitten(color: Int, isFat: Boolean)

val cat = Cat(color = Some(3), isFat = true, "SomeValue")

val genCat = Generic[Cat]
val genKit = Generic[Kitten]

import HListsFlatten._

scala> val kitten = genKit.from(flatten(genCat.to(cat)))
kitten: Kitten = Kitten(3,true)