我有一个包含几个字符串字段的案例类。我使用字符串拆分的值结果来实例化类,但当任何字段为空字符串时,我想得到错误。例如:
case class SomeClass(a: String, b: String)
val line = ",a"
val values = line.split(",")
SomeClass(values(0), values(1))
将导致:
SomeClass = SomeClass(,a)
显然,我可以单独检查每个元素是否为空,但是有没有更优雅和实用的方法可以防止在有空字段时实例化?
这个答案需要外部库,如果你想坚持核心 Scala,可能不适合你,但我会鼓励这样做,主要是因为构造函数是一个合约。这里说You give me 2 strings and I will give you SomeClass
即::String -> String -> SomeClass
.在我看来,即使输入符合标准,这也可能不会得到尊重,这一事实打破了合理性。有几件事,我会建议
1( 使用细化类型
import eu.timepit.refined.types.string._
import eu.timepit.refined._
import eu.timepit.refined.auto._
case class SomeClass(a: NonEmptyString, b: NonEmptyString)
将无法使用空字符串构造它。您可以对错误执行任何操作。
2(如果你不想使用精炼类型=>我认为Either
给了你更多的权力,关于为什么事情失败了。一个不错的选择是Validated
半组。
import cats.data._
import cats.data.Validated._
import cats.implicits._
sealed trait ValidationError {
def error: String
}
case object StringIsEmptyError extends ValidationError{
def error = "Argument Can't Be Empty String"
}
type ValidationResult[A] = ValidatedNec[ValidationError, A]
def validateArgument(s: String): ValidationResult[String] = if (s.nonEmpty) s.validNec else StringIsEmptyError.invalidNec
object SomeClass {
def apply(a: String, b: String) = (validateArgument(a), validateArgument(b)).mapN(SomeClass).toEither
}
这比您可能想要的代码多一点,但我认为如果您将来对参数有更多要求,则更容易推理 + 易于扩展。
与Option
或Either
相反,Validated的一个好处是,它允许您累积错误。因此,在这种情况下,如果两个参数都为空,则可以收集两个错误并向用户报告,而不是模棱两可地失败
试一试。
在伴随对象中创建一个或多个用于构造和验证预期参数的工厂方法。将主构造函数设为私有,以便无法绕过工厂方法。
case class SomeClass private (a: String, b: String)
object SomeClass {
def apply(ab: String): Option[SomeClass] = util.Try{
val Array(a, b) = ab.split(",")
apply(a, b)
}.getOrElse(None)
def apply(a: String, b: String): Option[SomeClass] =
if (a.isEmpty || b.isEmpty) None
else Some(new SomeClass(a, b))
}
测试:
SomeClass("aa,bb") //res0: Option[SomeClass] = Some(SomeClass(aa,bb))
SomeClass(",cc") //res1: Option[SomeClass] = None
SomeClass("g:g") //res2: Option[SomeClass] = None
new SomeClass("x", "y") //fails to compile
您可以使用应用函数定义配套对象。
case class SomeClass(a: String, b: String)
object SomeClass {
def apply(a: String, b: String): SomeClass = {
if (a.trim.isEmpty || b.trim.isEmpty) throw new IllegalArgumentException
SomeClass(a, b)
}
}
@ SomeClass("", "b")
java.lang.IllegalArgumentException
ammonite.$sess.cmd4$SomeClass$.apply(cmd4.sc:3)
ammonite.$sess.cmd5$.<init>(cmd5.sc:1)
ammonite.$sess.cmd5$.<clinit>(cmd5.sc)
case class SomeClass(a: String, b: String) {
require(a.nonEmpty && b.nonEmpty, "a and b cannot be empty")
}
您可以通过简单的要求来实现它。但是如果你想在编译时根据类型来限制它,你可以按照@sinanspd上述答案中的建议使用NonEmptyString
。
https://scastie.scala-lang.org/shankarshastri/uPk8ZMqCQUa5G3RcKfZtwA/