gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 17:54:25 25 4
gpt4 key购买 nike

对我来说,Scala 类型系统更令人困惑的方面之一是理解协变、逆变、类型界限等。

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

下面是代码的精简版本。我收到了 type mismatch调用 register(pages) 时出错.编译器发现 HashMap[String, T]但期待 HashMap[String, Page] .我不知道该怎么做才能让编译器满意。我可以将注册方法定义为 def register[T <: Page](repo: HashMap[String, T) ...但这只是将问题推迟到 var repos 的引用中我不能一般地限定。如果有人可以演示指定类型的正确方法,我将不胜感激。

编辑 如果我将哈希图声明为 HashMap[String, Page],我可以让它工作然后投 page从哈希图中检索的值 page.asInstanceOf[String, T] .有没有办法避免 Actor ?

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 是不变的: class HashMap [A, B] 。尽管不可变版本在值上是协变的: class HashMap [A, +B]

要注意的第二件事是您的 repos 变量是一个多态集合,这意味着当您将内容放在那里时,某些编译时类型信息会丢失。

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

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] 并且必须处理它。

更新 :通过使 .asInstance[T] Repositorypages 代码中删除 private[this]

关于scala - 为伴随对象中的方法指定类型差异的正确方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5268300/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com