加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 为伴随对象中的方法指定类型方差的正确方法是什么

发布时间:2020-12-16 10:01:36 所属栏目:安全 来源:网络整理
导读:对我来说,Scala类型系统更令人困惑的一个方面是理解协方差,逆变,类型界限等. 我正在尝试创建一个通用的Repository特征,可以通过扩展Page特征的类的伴随对象对象进行扩展.我们的想法是,伴随对象将负责创建新实例等.如果在一段时间内未访问这些页面实例,则需要
对我来说,Scala类型系统更令人困惑的一个方面是理解协方差,逆变,类型界限等.

我正在尝试创建一个通用的Repository特征,可以通过扩展Page特征的类的伴随对象对象进行扩展.我们的想法是,伴随对象将负责创建新实例等.如果在一段时间内未访问这些页面实例,则需要清理这些实例.因此,基本存储库特征会将它们注册到可以在后台actor线程中检查的存储库列表中.

下面是代码的精简版本.我在注册(页面)调用时遇到类型不匹配错误.编译器找到了HashMap [String,T]但是期待HashMap [String,Page].我无法弄清楚如何使编译器满意.我可以将寄存器方法定义为def寄存器[T<:Page](repo:HashMap [String,T] ...但是这只是将问题推迟到var repos的引用,我不能一般地限定它.如果有人能够证明指定类型的正确方法,我将不胜感激. 编辑如果我将hashmap声明为HashMap [String,Page]然后使用page.asInstanceOf [String,T]转换从hashmap检索的页面值,我可以使它工作.有没有办法避免演员?

trait Page {
  val id = Random.hex(8)
  private var lastAccessed = new Date
  ...
}

object Page {
  import scala.collection.mutable.HashMap

  trait Repository[T <: Page] {
    private val pages = new HashMap[String,T]
    register(pages)

    def newPage: T

    def apply(): T = {
      val page = newPage
      pages(page.id) = page
      page
    }

    def apply(id: String): T = {
      pages.get(id) match {
        case Some(page) =>
          page.lastAccessed = now
          page
        case None =>
          this()
      }
    }
    ...
  }

  private var repos: List[HashMap[String,Page]] = Nil

  private def register(repo: HashMap[String,Page]) {
    repos = repo :: repos
  }
  ...
}

class CoolPage extends Page

object CoolPage extends Page.Repository[CoolPage] {
  def newPage = new CoolPage
}

val p = CoolPage()

解决方法

首先要注意的是可变HashMap是不变的:类HashMap [A??,B].虽然不可变版本在值上是协变的:class HashMap [A??,B].

需要注意的第二件事是你的repos变量意味着是一个多态集合,这意味着当你把东西放在那里时,一些编译时类型信息会丢失.

但是由于你使用了可变的HashMap,因为HashMap的不变性,repos实际上不能是一个正确的多态集合.为了说明为什么让我们假设Page是一个类(所以我们可以立即它),我们在repos列表中放入一个HashMap [String,CoolPage].然后我们可以这样做:

val m = repos.head // HashMap[String,Page]
m.put("12345678",new Page) // We just added a Page to HashMap[String,CoolPage]

所以编译器会给你一个错误,以保护你免受此事.

我想你可以通过使Repository协变来修复你的代码:

trait Repository[+T <: Page] {
  private[this] val pages = new HashMap[String,T]
  register(this)

  def newPage: T

  def apply(): T = {
    val page = newPage
    pages(page.id) = page
    page
  }

  def apply(id: String): T = {
    pages.get(id) match {
      case Some(page) =>
        page.lastAccessed = new Date
        page
      case None =>
        this()
    }
  }
}

并将repos更改为Repository [Page]列表:

private var repos: List[Repository[Page]] = Nil

private def register(repo: Repository[Page]) {
  repos = repo :: repos
}

并且记住,多态集合(如repos)会让你丢失元素的编译时类型信息:如果你把Repository [CoolPage]放在那里你只能得到Repository [Page]并且必须处理它.

更新:通过将页面设为私有[this]从存储库代码中删除.asInstance [T].

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读