我有一些由三个字段组成的数据,比如说String,Int和Double.
它们都是可选的,但整个事物必须至少包含其中一个字段.
我试过cats.data.Ior,比如MyType = String Ior Int Ior Double.它有效,但实际上用它做东西时感觉有点笨拙.创建一个包含三个Options的case类并在构造函数中抛出异常(如果它们都是None)也应该有用,但我不喜欢这个想法.
我很想知道是否有其他方法可以做到这一点(最好是通过仅在类似Ior的类型中编码整个信息,但复杂性较低,以便使用类型的值不那么复杂).建议您使用任何其他图书馆.
最佳答案 你可能也想过这个想法,但是这样的事情怎么样:
class StringOrIntOrDouble private(val first: Option[String], val second: Option[Int], val third: Option[Double]) {
def this(first: String) = this(Some(first), None, None)
def this(second: Int) = this(None, Some(second), None)
def this(third: Double) = this(None, None, Some(third))
def this(first: String, second: Int) = this(Some(first), Some(second), None)
def this(second: Int, third: Double) = this(None, Some(second), Some(third))
def this(first: String, third: Double) = this(Some(first), None, Some(third))
def this(first: String, second: Int, third: Double) = this(Some(first), Some(second), Some(third))
}
主构造函数是私有的,因此您无法创建空实例.显然,您可以使用Product和equals,hashCode,toString以及其他有用的东西(如案例类)来实现扩展它(但要注意可能轻易破坏不变量的副本).
不幸的是,如果你想要一个通用版本(或者你在两个地方有相同的类型),你必须将所有“子集”构造函数移动到一个配对对象的命名方法,否则它将因为类型擦除而无法编译.
class TripleIor[+A, +B, +C] private(val first: Option[A], val second: Option[B], val third: Option[C]) {
}
object TripleIor {
def first[A](first: A) = new TripleIor[A, Nothing, Nothing](Some(first), None, None)
def second[B](second: B) = new TripleIor[Nothing, B, Nothing](None, Some(second), None)
def third[C](third: C) = new TripleIor[Nothing, Nothing, C](None, None, Some(third))
def firstAndSecond[A, B](first: A, second: B) = new TripleIor[A, B, Nothing](Some(first), Some(second), None)
def secondAndThird[B, C](second: B, third: C) = new TripleIor[Nothing, B, C](None, Some(second), Some(third))
def firstAndThird[A, C](first: A, third: C) = new TripleIor[A, Nothing, C](Some(first), None, Some(third))
def apply[A, B, C](first: A, second: B, third: C) = new TripleIor[A, B, C](Some(first), Some(second), Some(third))
}
一种更激进的方式是实现与密封特征和7个子类相同的想法,但我认为它们不会更容易使用.此外,它还允许您以类型安全的方式实现复制,但需要大量输入的成本(此处未显示).
sealed trait TripleIor[A, B, C] extends Product {
def firstOption: Option[A]
def secondOption: Option[B]
def thirdOption: Option[C]
}
object TripleIor {
final case class First[A](first: A) extends TripleIor[A, Nothing, Nothing] {
override def firstOption: Option[A] = Some(first)
override def secondOption: Option[Nothing] = None
override def thirdOption: Option[Nothing] = None
}
final case class Second[B](second: B) extends TripleIor[Nothing, B, Nothing] {
override def firstOption: Option[Nothing] = None
override def secondOption: Option[B] = Some(second)
override def thirdOption: Option[Nothing] = None
}
final case class Third[C](third: C) extends TripleIor[Nothing, Nothing, C] {
override def firstOption: Option[Nothing] = None
override def secondOption: Option[Nothing] = None
override def thirdOption: Option[C] = Some(third)
}
final case class FirstSecond[A, B](first: A, second: B) extends TripleIor[A, B, Nothing] {
override def firstOption: Option[A] = Some(first)
override def secondOption: Option[B] = Some(second)
override def thirdOption: Option[Nothing] = None
}
final case class SecondThird[B, C](second: B, third: C) extends TripleIor[Nothing, B, C] {
override def firstOption: Option[Nothing] = None
override def secondOption: Option[B] = Some(second)
override def thirdOption: Option[C] = Some(third)
}
final case class FirstThird[A, C](first: A, third: C) extends TripleIor[A, Nothing, C] {
override def firstOption: Option[A] = Some(first)
override def secondOption: Option[Nothing] = None
override def thirdOption: Option[C] = Some(third)
}
final case class All[A, B, C](first: A, second: B, third: C) extends TripleIor[A, B, C] {
override def firstOption: Option[A] = Some(first)
override def secondOption: Option[B] = Some(second)
override def thirdOption: Option[C] = Some(third)
}
}
附:请注意,这里的所有示例都只是草图,以说明不实现许多有用甚至是必需的东西的想法