Scala-实例化类时处理空字符串

Scala-实例化类时处理空字符串,scala,Scala,我有一个带有两个字符串字段的case类。我使用字符串拆分的结果值来实例化该类,但希望在任何字段为空字符串时得到一个错误。例如: case class SomeClass(a: String, b: String) val line = ",a" val values = line.split(",") SomeClass(values(0), values(1)) 将导致: SomeClass = SomeClass(,a) 显然,我可以单独检查每个元素是否为空,但是当存在空字段时,有没有更

我有一个带有两个字符串字段的case类。我使用字符串拆分的结果值来实例化该类,但希望在任何字段为空字符串时得到一个错误。例如:

case class SomeClass(a: String, b: String)
val line = ",a"
val values = line.split(",")
SomeClass(values(0), values(1))
将导致:

SomeClass = SomeClass(,a)

显然,我可以单独检查每个元素是否为空,但是当存在空字段时,有没有更优雅和实用的方法来防止实例化?

您可以使用apply函数定义伴生对象

case类SomeClassa:String,b:String 对象类{ def applya:String,b:String:SomeClass={ 如果a.trim.isEmpty | | b.trim.isEmpty抛出新的非法辩论例外 A级、b级 } } 试试这个

在伴生对象中,创建一个或多个工厂方法,用于构造和验证预期参数。将主构造函数设为私有,以便无法绕过工厂方法

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
这个答案需要外部库,如果您想坚持使用核心Scala,可能不适合您,但我鼓励您这样做,主要是因为构造函数是一个契约。这里说你给我两个字符串,我给你一些类,比如::字符串->字符串->一些类。在我看来,即使输入符合标准,这一点也可能得不到尊重,这破坏了可靠性。有几件事,我建议

1.使用精制类型

这样就不可能用空字符串构造它。你可以用这个错误做任何你想做的事情

2如果你不想使用精炼类型=>我认为这两种类型都会给你带来更多的力量,比如为什么事情会失败。一个好的选择是验证半群

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或两者相反,Validated的一个优点是,它允许您累积错误。因此,在本例中,如果两个参数都为空,则可以收集两个错误并向用户报告,而不是含糊不清地失败

case class SomeClass(a: String, b: String) {
  require(a.nonEmpty && b.nonEmpty, "a and b cannot be empty")
}
你可以通过简单的要求来实现它。但是,如果您想在编译时根据类型对其进行严格限制,可以按照@sinanspd在上述答案中的建议使用NonEmptyString


首选的预防措施是什么?编译失败?抛出运行时异常?返回选项[SomeClass]?返回null?@jwvh Returning Option[SomeClass],with None如果任何值为空,则将被视为理想的选择。在框外思考一点,为什么不使用一个精致的类型nonemptysting呢?我觉得非常不直观,要将此功能委托给构造函数,我希望避免使用额外的库,然而,这个答案很有教育意义。这个解决方案非常简单,我喜欢它,但是它在试图编译项目时会引起错误。对重载定义的引用不明确,[error]对象SomeClass中的方法apply和对象SomeClass中的方法apply都匹配参数类型String、String和预期结果类型SomeClass。我喜欢这种方法,因为它在内部进行拆分。但是,在终端上粘贴它时,我会遇到以下错误。可能是因为我使用的是较旧版本的scala。@Jack,您遇到了什么错误?您运行的是什么版本?这在Scala 2.12.7和2.13.1上测试很好。抱歉,忘记粘贴它:错误:类SomeClass中的构造函数SomeClass无法在对象SomeClass中访问其他SomeClass Somenew SomeClassa,b。我用的是2.11.12mm,2.11.12已经很旧了。我无法重现这个错误。您可能不得不放弃私有构造函数。
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
}
case class SomeClass(a: String, b: String) {
  require(a.nonEmpty && b.nonEmpty, "a and b cannot be empty")
}