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

Yan*_*san 3 casting scala

根据设计,我们确信我们有一个 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)
    }
  }
Run Code Online (Sandbox Code Playgroud)

Dmy*_*tin 5

尝试再添加一个隐式参数

def getPath(date: LocalDateTime)(implicit ev: S =:= Hourly.type, ev1: DateFormat[S] =:= HourlyDateFormat): String = {
  //assert(ev == ev)
  dateFormat.format(date)
}
Run Code Online (Sandbox Code Playgroud)

断言看起来很奇怪:assert(ev == ev)

要不就

def getPath(date: LocalDateTime)(implicit ev1: DateFormat[S] =:= HourlyDateFormat): String
Run Code Online (Sandbox Code Playgroud)

固定版本(我又添加了一个类型参数,它现在类似于第一个@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()))
Run Code Online (Sandbox Code Playgroud)

  • 该断言可能是为了抑制未使用的警告。在你的第二个例子中,不需要。 (3认同)
  • 鉴于“DailyDateFormat”可能有多个子类(这就是它是抽象的原因),我用“&lt;:&lt;”更改了“=:=”,但它不起作用。`new Log(Hourly, DateFormat.HOURLY_LEADING_ZEROS).getPath(LocalDate.now))`。无法证明 DateFormat[Hourly.type] &lt;:&lt; HourlyDateFormat。 (2认同)

Mat*_*zok 5

通常这样的事情有点类型优雅,所以我会这样做:

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)
}
Run Code Online (Sandbox Code Playgroud)

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