我正在嘗試創建一個案例類的型別安全構建器,其中它的引數可以是以下型別:
- 必需的
- 選修的
- 必需但互斥 -> a。前任。假設我有 3 個引數:(param1),(param2,param3)。如果我有 param1,我無法設定 param2 或 param3。如果我可以同時設定 param2 和 param3,但我不能設定 param1
- 可選但互斥 -> 與上述邏輯相同,但這些是可選引數。這是可選的,我可以設定 param1 或(param2 和 param3)。
我想出了如何獲得必需和可選案例,但無法獲得案例 3 和 4。任何想法如何繼續。
這些檢查可以在運行時完成,但我希望在編譯時實作這些規則集
case class Person(name: String, /*required*/
address: String, /*optional*/
city: String, /* reqd exclusive*/
county: String, /* reqd exclusive*/
state: String /* reqd exclusive*/,
ssn: String, /* optional exclusive*/
insurance: String, /* opt exclusive*/
passport: String /* opt exclusive*/)
// where (city) and (county, state) are required but are mutually exclusive
// (ssn) and (insurance, passport) are optional but are mutually exclusive.
// If I set passport, I've to set insurance
sealed trait PersonInfo
object PersonInfo {
sealed trait Empty extends PersonInfo
sealed trait Name extends PersonInfo
sealed trait Address extends PersonInfo
type Required = Empty with Name with Address
}
case class PersonBuilder[T <: PersonInfo]
(name: String = "", address: String = "", city: String = "", county: String = "",
state: String = "", ssn: String = "", insurance: String = "",passport: String ="") {
def withName(name: String): PersonBuilder[T with PersonInfo.Name] =
this.copy(name = name)
def withTask(address: String): PersonBuilder[T with PersonInfo.Address ] =
this.copy(address = address)
def withCity(city: String): PersonBuilder[T] =
this.copy(city = city)
def withCountry(county: String): PersonBuilder[T] =
this.copy(county = county)
def withState(state: String): PersonBuilder[T] =
this.copy(state = state)
def withSsn(ssn: String): PersonBuilder[T] =
this.copy(ssn = ssn)
def withInsurance(insurance: String): PersonBuilder[T] =
this.copy(insurance = insurance)
def withPassport(passport: String): PersonBuilder[T] =
this.copy(passport = passport)
def build(implicit ev: T =:= PersonInfo.Required): Person =
Person(name, address, city, county, state, ssn, insurance, passport)
}
這是構建
val testPerson = PersonBuilder[PersonInfo.Empty]()
.withName("foo")
.withSsn("bar")
uj5u.com熱心網友回復:
正如評論中提到的,如果創建構建器不是一個硬性要求,一個可行的選擇可能是在型別中明確這些要求,使用 sum 型別作為獨占選擇,使用Options 作為可選型別,如下例所示:
sealed abstract class Location extends Product with Serializable {
def value: String
}
object Location {
final case class City(value: String) extends Location
final case class County(value: String) extends Location
final case class State(value: String) extends Location
}
sealed abstract class Identity extends Product with Serializable {
def value: String
}
object Identity {
final case class Ssn(value: String) extends Identity
final case class Insurance(value: String) extends Identity
final case class Passport(value: String) extends Identity
}
final case class Person(
name: String,
address: Option[String],
location: Location,
identity: Option[Identity],
)
Scala 3 進一步引入了enums 使得定義更加緊湊和可讀:
enum Location(value: String) {
case City(value: String) extends Location(value)
case County(value: String) extends Location(value)
case State(value: String) extends Location(value)
}
enum Identity(value: String) {
case Ssn(value: String) extends Identity(value)
case Insurance(value: String) extends Identity(value)
case Passport(value: String) extends Identity(value)
}
final case class Person(
name: String,
address: Option[String],
location: Location,
identity: Option[Identity],
)
并且將Options 默認設定為None您可以獲得與定制構建器非常相似的體驗,而無需任何額外代碼:
final case class Person(
name: String,
location: Location,
address: Option[String] = None,
identity: Option[Identity] = None,
)
Person("Alice", Location.City("New York"))
.copy(identity = Some(Identity.Ssn("123456")))
您可以很容易地進一步細化:
final case class Person(
name: String,
location: Location,
address: Option[String] = None,
identity: Option[Identity] = None
) {
def withAddress(address: String): Person =
this.copy(address = Some(address))
def withIdentity(identity: Identity): Person =
this.copy(identity = Some(identity))
}
Person("Alice", Location.City("New York")).withIdentity(Identity.Ssn("123456"))
您可以在 Scastie 上使用此代碼。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/422315.html
標籤:
上一篇:將泛型定義為案例類
