如何避免在 Scala 中调用具有族多态性的 asInstanceOf



根据设计,我们确信我们有一个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)
}
}

通常,这样的事情是类型类的,所以我会这样做:

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)
}

它的优点是,您不必知道HourlyDateFormatDailyDateFormat类型的存在即可使其工作。

尝试再添加一个隐式参数

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()))

快速修复将是这样的:

class Log[S <: StorageLayout, D <: DateFormat[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDateTime)(implicit ev: D <:< HourlyDateFormat): String =
dateFormat.format(date)
}

但是,我认为您没有以正确的方式设计它。最好为每种格式设置单独的特征。这使得它更具可扩展性,因为您不需要在每个不同的类的匹配表达式中添加大小写,因此会在运行时自动选择正确的方法。你仍然必须使用这些证据参数,我不喜欢这些参数,但对我来说仍然感觉更干净。

编辑:我已经更新了代码,以便一切都扩展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)
}

斯卡斯蒂

最新更新