首页 文章

将带有Options的案例类转换为不带选项的案例类

提问于
浏览
3

具有嵌套选项的案例类如何通过提取 Some 并将默认值替换为_1696882来将其转换为没有选项的双案例类 .

假设我们有:

case class UserPreferencesOpt(animals: Option[AnimalPreferencesOpt])
case class AnimalPreferencesOpt(dogs: Option[String], cats: Option[String])

case class UserPreferences(animals: AnimalPreferences)
object UserPreferences {
    def getDefault: UserPreferences(AnimalPreferences("bark", "meow")
}
case class AnimalPreferences(dogs: String, cats: String)

所以基本上,我想转换:

UserPreferencesOpt(Some(AnimalPreferencesOpt(Some("Dogs are cool!"), None)))

成:

UserPreferences(AnimalPreferences("Dogs are cool!", "meow")

通过在存在时提取选项并替换缺少默认值 .

它可以通过模式匹配和按值检查来完成,但这只是一个例子 . 显然,在很多情况下,域逻辑中会有更多的嵌套首选项,我想知道是否可以转换它而不添加额外的匹配和添加新首选项的情况(例如 WeatherPreferences 甚至是UserPreferences中的某些内容) .

听说过像Shapeless或宏这些高级Scala之类的东西,我觉得有人可能会推荐它,但有没有办法以惯用,功能,Scala-ish方式实现它?

4 回答

  • 1

    您可以在伴随对象的 apply 中为目标案例类指定默认值 . 例如 . :

    case class UserPreferencesOpt(animals: Option[AnimalPreferencesOpt] = None)
    case class AnimalPreferencesOpt(dogs: Option[String] = None, cats: Option[String] = None)
    
    case class UserPreferences(animals: AnimalPreferences)
    object UserPreferences {
      def apply(o: UserPreferencesOpt): UserPreferences = {
        UserPreferences(
          AnimalPreferences(
            o.animals.getOrElse(AnimalPreferencesOpt())
          ))
      }
    }
    case class AnimalPreferences(dogs: String, cats: String)
    
    object AnimalPreferences {
      def apply(opt: AnimalPreferencesOpt): AnimalPreferences = {
        AnimalPreferences(
          opt.dogs.getOrElse("bark"),
          opt.cats.getOrElse("meow")
        )
      }
    }
    

    但我同意@ pedrorijo91那个模型看起来很奇怪..

  • 1

    如果默认值是静态的,那么更简单的解决方案是在实例构造中填充它,而不是具有Options的类 .

    对于你的例子,那将是:

    case class UserPreferences(animals: AnimalPreferences)
    case class AnimalPreferences(dogs: String, cats: String)
    
    object UserPreferences {
      def apply(dogOpt: Option[String], catOpt: Option[String]): UserPreferences = {
        val dog = dogOpt.getOrElse("bark")
        val cat = catOpt.getOrElse("meow")
        UserPreferences(AnimalPreferences(dog, cat))
      }
    }
    
    // then you can create, eg:
    UserPreferences(None, None)
    UserPreferences("Grrr", None)
    UserPreferences("Dogs are cool!", "meow")
    
  • 1

    另一种解决此问题的scala-ish方法是使用 implicit conversions . 你必须定义 implicit methods ,将toConvert类转换为所需的类,例如 UserPreferenceOptUserPreference 各自的同伴 class .

    case class UserPreferencesOpt(animals: Option[AnimalPreferencesOpt])
      case class AnimalPreferencesOpt(dogs: Option[String], cats: Option[String])
    
      case class UserPreferences(animals: AnimalPreferences)
      case class AnimalPreferences(dogs: String, cats: String)
    
      //Note that, I have used `null` value in case of `None` found in Opt class. Instead of `null`, you can provide default value with some logic here.
      object UserPreferencesOpt {
        implicit def optToUserPref(userPref: UserPreferencesOpt): UserPreferences = UserPreferences(userPref.animals.getOrElse(null))
      }
      object AnimalPreferencesOpt {
        implicit def optToAnimalPref(animalPref: AnimalPreferencesOpt): AnimalPreferences = AnimalPreferences(animalPref.dogs.getOrElse(null), animalPref.cats.getOrElse(null))
      }
      val userPrefOpt:UserPreferencesOpt = UserPreferencesOpt(Some(AnimalPreferencesOpt(Some("Dogs are cool!"), None)))
      val userPref: UserPreferences = userPrefOpt
    
  • 1

    有一个library支持此功能,如下所示:

    import cats.data.Validated
    import cats.implicits._
    import henkan.optional.all._
    
    case class Message(a: Option[String], b: Option[Int])
    case class Domain(a: String, b: Int)
    
    validate(Message(Some("a"), Some(2))).to[Domain]
    // res0: henkan.optional.ValidateFromOptional.Result[Domain] = Valid(Domain(a,2))
    
    validate(Message(Some("a"), None)).to[Domain]
    // res1: henkan.optional.ValidateFromOptional.Result[Domain] = Invalid(NonEmptyList(RequiredFieldMissing(b)))
    

相关问题