Scala refined库:如何安全拼接NonEmptyString无需unsafeFrom方法?
问题
我正在使用refined库中的NonEmptyString类型。当拼接两个字符串(其中至少一个为非空)时,结果显然也是一个非空字符串。但有没有办法在不使用NonEmptyString.unsafeFrom这类不安全方法的前提下,让Scala编译器认可这一事实?
解决方案
可以通过以下几种安全方式实现,完全不需要依赖不安全的unsafeFrom操作:
1. 给NonEmptyString扩展安全拼接方法
利用refined的编译期宏refineMV,给NonEmptyString和普通String编写扩展方法。因为已知NonEmptyString本身非空,拼接任何字符串后的结果必然非空,宏会在编译期直接验证这一事实,生成合法的NonEmptyString实例,无运行时检查开销。
import eu.timepit.refined._ import eu.timepit.refined.types.string.NonEmptyString import eu.timepit.refined.collection.NonEmpty import scala.language.implicitConversions // NonEmptyString拼接普通String extension (nes: NonEmptyString) { def +(s: String): NonEmptyString = refineMV[NonEmpty](nes.value + s) } // 普通String拼接NonEmptyString extension (s: String) { def +(nes: NonEmptyString): NonEmptyString = refineMV[NonEmpty](s + nes.value) } // 使用示例 val hello: NonEmptyString = refineV[NonEmpty]("Hello").right.get // 安全初始化 val world = " World" val combined: NonEmptyString = hello + world // 编译通过,无unsafe操作
2. 编译期约束推导(处理至少一个非空的场景)
如果你的场景是两个字符串都可能为空,但能保证至少一个非空,可以用refined的Or逻辑约束定义类型条件,让编译器在编译期检查该条件,只有满足时才允许生成NonEmptyString。
import eu.timepit.refined._ import eu.timepit.refined.types.string.NonEmptyString import eu.timepit.refined.collection.NonEmpty import eu.timepit.refined.logical.Or // 定义约束:两个字符串中至少一个非空 type AtLeastOneNonEmpty = NonEmpty Or NonEmpty def concatSafe(a: String, b: String)(implicit ev: Refined[(String, String), AtLeastOneNonEmpty]): NonEmptyString = { refineMV[NonEmpty](a + b) } // 合法调用(至少一个非空) val valid1 = concatSafe("Hello", "") val valid2 = concatSafe("", "World") // 非法调用(两个都为空)会触发编译错误 // val invalid = concatSafe("", "")
3. 直接使用refineV的编译期推导
如果能在代码中明确证明拼接后的字符串非空(比如其中一个是已知的NonEmptyString),可以直接用refineV的编译期推导,它会自动验证并返回Right(NonEmptyString),无需运行时检查。
import eu.timepit.refined._ import eu.timepit.refined.types.string.NonEmptyString import eu.timepit.refined.collection.NonEmpty val nes: NonEmptyString = refineV[NonEmpty]("foo").right.get val s = "bar" // 编译期推导拼接结果非空,直接获取Right值 val combined: NonEmptyString = refineV[NonEmpty](nes.value + s).right.get
内容的提问来源于stack exchange,提问作者Matthias Berndt
相关产品推荐
相关产品推荐

