scala – 为伴随对象中的方法指定类型方差的正确方法是什么
对我来说,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]. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |