[英]How to avoid calling asInstanceOf in Scala with family polymorphism
按照設計,我們確定我們有一個HourlyDateFormat
實例
在這種情況下如何避免調用 asInstanceOf (即如何幫助編譯器推斷類型)?
sealed trait StorageLayout extends Product with Serializable
case object Hourly extends StorageLayout
case object Daily extends StorageLayout
sealed trait DateFormat[S <: StorageLayout]
sealed abstract class HourlyDateFormat extends DateFormat[Hourly.type] {
def format(localDate: LocalDate): String = ???
def format(localDateTime: LocalDateTime): String = ???
}
sealed abstract class DailyDateFormat extends DateFormat[Daily.type] {
def format(localDate: LocalDate): String = ???
}
class Log[S <: StorageLayout](storageLayout: S, dateFormat: DateFormat[S]) {
def getPath(date: LocalDate): String =
dateFormat match {
case hdf: HourlyDateFormat => hdf.format(date)
case ddf: DailyDateFormat => ddf.format(date)
}
@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
def getPath(date: LocalDateTime)(implicit ev: S =:= Hourly.type): String = {
assert(ev == ev)
dateFormat.asInstanceOf[HourlyDateFormat].format(date)
}
}
嘗試再添加一個隱式參數
def getPath(date: LocalDateTime)(implicit ev: S =:= Hourly.type, ev1: DateFormat[S] =:= HourlyDateFormat): String = {
//assert(ev == ev)
dateFormat.format(date)
}
斷言看起來很奇怪:
assert(ev == ev)
。
要不就
def getPath(date: LocalDateTime)(implicit ev1: DateFormat[S] =:= HourlyDateFormat): String
固定版本(我增加了一個類型參數,它現在類似於第一個@user的版本)
class Log[S <: StorageLayout, D <: DateFormat[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDate): String =
dateFormat match {
case hdf: HourlyDateFormat => hdf.format(date)
case ddf: DailyDateFormat => ddf.format(date)
}
def getPath(date: LocalDateTime)(implicit
ev: S =:= Hourly.type,
ev1: D <:< HourlyDateFormat,
): String = {
dateFormat.format(date)
}
}
val log = new Log(Hourly, new HourlyDateFormat(){})
print(log.getPath(LocalDateTime.now()))
一般來說,這樣的事情是一種典型的類型,所以我會這樣做:
trait DailyFormatter[S] {
def formatDate(localDate: LocalDate): String
}
trait HourlyFormatter[S] {
def formatDateTime(localDateTime: LocalDateTime): String
}
implicit val dailyFormats: DailyFormatter[Daily]
implicit val hourFormats: DailyFormatter[Hourly] with HourlyFormatter[Hourly]
class Log[S <: StorageLayout](storageLayout: S, dateFormat: DateFormat[S]) {
def getPath(date: LocalDate)(implicit formater: DailyFormatter[S]): String =
formater.formatDate(date)
def getPath(date: LocalDateTime)(implicit formater: HourlyFormatter[S]): String =
formater.formatDateTime(date)
}
它的優點是您不必知道類型HourlyDateFormat
和DailyDateFormat
的存在即可使其工作。
快速修復將是這樣的:
class Log[S <: StorageLayout, D <: DateFormat[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDateTime)(implicit ev: D <:< HourlyDateFormat): String =
dateFormat.format(date)
}
但是,我認為您的設計方式不正確。 為每種類型的格式設置一個單獨的特征可能會更好。 這使其更具可擴展性,因為您不需要在匹配表達式中為每個不同的 class 添加大小寫,因此在運行時會自動選擇正確的方法。 你仍然必須使用那些我不喜歡的證據參數,但對我來說它仍然感覺更干凈。
編輯:我已經更新了代碼,以便所有內容都擴展FormatLocalDate
並且您只需要getPath(LocalDateTime)
的證據參數
sealed trait FormatLocalDate[S <: StorageLayout] {
def format(localDate: LocalDate): String
}
sealed trait FormatLocalDateTime[S <: StorageLayout] extends FormatLocalDate[S] {
def format(localDate: LocalDateTime): String
}
sealed abstract class HourlyDateFormat extends FormatLocalDateTime[Hourly.type] {
def format(localDate: LocalDate): String = ???
def format(localDateTime: LocalDateTime): String = ???
}
sealed abstract class DailyDateFormat extends FormatLocalDate[Daily.type] {
def format(localDate: LocalDate): String = ???
}
class Log[S <: StorageLayout, D <: FormatLocalDate[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDate): String =
dateFormat.format(date)
def getPath(date: LocalDateTime)(implicit ev: D <:< FormatLocalDateTime[S]): String =
dateFormat.format(date)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.