使用Scala Cats校验类的非空字段组合有效性
嘿,这个需求用Scala Cats来实现其实挺顺畅的,我来给你拆解一下思路和具体代码,帮你搞定这个校验逻辑!
首先,我们的核心目标是:只要Test实例满足任意一组指定的字段非空组合,校验就通过;如果所有组合都不满足,就把所有失败的组合信息返回给用户。Cats的Validated类型正好适合这种需要错误累积的场景,同时我们可以利用Scala的元组和Option特性来简化字段非空检查。
步骤1:导入必要的Cats依赖和定义Test类
首先确保你已经引入了Cats核心库,然后定义你的样例类:
import cats.data.Validated import cats.syntax.all._ case class Test(a: Option[String], b: Option[String], c: Option[String], d: Option[String], e: Option[Double], f: Option[Double])
步骤2:为每个有效组合编写校验函数
我们为每个指定的字段组合单独写一个校验逻辑:用tupled把多个Option组合成一个Option元组——只有当所有字段都是Some时,tupled才会返回Some,否则是None。然后用toValid把这个Option转换成Validated,失败时返回对应的错误信息:
object TestValidator { // 校验组合1: a、b、c全部非空 private def validateGroup1(test: Test): Validated[String, Unit] = (test.a, test.b, test.c).tupled.toValid("组合a,b,c未全部非空").void // 校验组合2: a、d、e全部非空 private def validateGroup2(test: Test): Validated[String, Unit] = (test.a, test.d, test.e).tupled.toValid("组合a,d,e未全部非空").void // 校验组合3: a、f全部非空 private def validateGroup3(test: Test): Validated[String, Unit] = (test.a, test.f).tupled.toValid("组合a,f未全部非空").void
步骤3:组合所有校验逻辑,实现“至少满足一组”的规则
接下来把三个校验函数的结果组合起来:只要有一个校验通过,整体就通过;如果所有都失败,就收集所有错误信息。这里用foldLeft遍历所有校验结果,遇到Valid就直接返回(提前终止,不用检查剩下的组合),否则累加错误:
// 主校验函数:至少满足一个组合则通过,否则返回所有失败信息 def validate(test: Test): Validated[List[String], Unit] = { val validations = List(validateGroup1(test), validateGroup2(test), validateGroup3(test)) validations.foldLeft(Invalid(List.empty[String]): Validated[List[String], Unit]) { case (_, valid @ Valid(_)) => valid // 找到一个通过的,直接返回 case (Invalid(errs), Invalid(err)) => Invalid(errs :+ err) // 收集失败信息 case (acc, _) => acc } } }
测试一下效果
我们可以写几个测试用例验证逻辑:
// 测试用例1:满足组合a,b,c → 校验通过 val test1 = Test(Some("a"), Some("b"), Some("c"), None, None, None) println(TestValidator.validate(test1)) // 输出: Valid(()) // 测试用例2:满足组合a,f → 校验通过 val test2 = Test(Some("a"), None, None, None, None, Some(1.0)) println(TestValidator.validate(test2)) // 输出: Valid(()) // 测试用例3:所有组合都不满足 → 返回所有失败信息 val test3 = Test(None, Some("b"), Some("c"), Some("d"), None, None) println(TestValidator.validate(test3)) // 输出: Invalid(List(组合a,b,c未全部非空, 组合a,d,e未全部非空, 组合a,f未全部非空)) // 测试用例4:满足多个组合 → 校验通过 val test4 = Test(Some("a"), Some("b"), Some("c"), Some("d"), Some(2.0), Some(3.0)) println(TestValidator.validate(test4)) // 输出: Valid(())
额外优化:类型安全的错误信息
如果不想用字符串作为错误信息,也可以定义自定义的错误枚举,让校验更类型安全:
sealed trait ValidationError case object Group1Failed extends ValidationError case object Group2Failed extends ValidationError case object Group3Failed extends ValidationError // 然后修改校验函数的返回类型: private def validateGroup1(test: Test): Validated[ValidationError, Unit] = (test.a, test.b, test.c).tupled.toValid(Group1Failed).void // 主函数返回Validated[List[ValidationError], Unit],这样错误信息更清晰且类型安全
这样整个逻辑就清晰又简洁了,完全利用了Cats的Validated和Scala的元组特性来实现需求~
内容的提问来源于stack exchange,提问作者cardosojc
相关产品推荐
相关产品推荐

