Первое, что нужно отметить, это то, что изменяемый 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]
Итак, компилятор выдаст вам ошибку, чтобы защитить вас от этого.
Полагаю, вы можете исправить свой код, сделав репозиторий ковариантным:
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]
из кода Repository
путем создания pages
private[this]
.