Scala Модификаторы и параметризация типа
Я создаю класс memoization.
Каждый класс запоминает тип функции и имеет следующее определение:
class MemoizedFunction1[-T1, +R](f: T1 => R) {
private[this] val cache = mutable.Map[T1, R]()
def apply(t: T1): R = cache.getOrElseUpdate(t,f(t))
}
Это компилируется красиво и работает так, как ожидалось.
Однако, если я удаляю измененный private[this]
, я получаю следующую ошибку:
contravariant type T1 occurs in invariant position in type => scala.collection.mutable.Map[T1,R] of value cache
Почему это, когда я удаляю модификатор, вдруг контравариантный тип T1 вмешивается в инвариантный тип Карты?
Как модификаторы влияют на параметризацию типа?
Ответы
Ответ 1
Не то, чтобы я все это понял, но это описано в разделе 4.5 (вариации аннотации) Scala Language Specification 2.9 on страница 45
Ссылки на параметры типа в объектно-частных или объектно-защищенных значениях, переменных или методах (§5.2) класса не проверяются для их позиции дисперсии. В эти члены параметр типа может появляться где угодно, не ограничивая его юридические аннотации на отклонения.
Чтобы упростить ваш пример, согласно спецификации, это нормально:
class Inv[T]
class Foo[-T] {
private[this] val a: Inv[T] = sys.error("compiles")
protected[this] val b: Inv[T] = sys.error("compiles")
}
Но если вы удалите [this]
, он будет жаловаться. На некотором уровне это имеет смысл, поскольку, если он не является частным или защищенным объектом, контравариантный тип возврата может протекать вне объекта и вызывать ошибку времени выполнения.
Ответ 2
Предположим, вы можете удалить [this]
.
Без [this]
вы можете добавить метод getOtherCache
:
class MemoizedFunction1[-T1, +R](f: T1 => R) {
private val cache = mutable.Map[T1, R]() // trait Map[A, B] extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
def apply(t: T1): R = cache.getOrElseUpdate(t,f(t))
def getOtherCache(other: MemoizedFunction1[T1, R]) {
val otherCache: mutable.Map[T1, R] = other.cache;
}
}
class A
class B extends A
val mf1: MemoizedFunction1[B, B] = new MemoizedFunction1[B, B](b => b)
val mf2: MemoizedFunction1[B, B] = new MemoizedFunction1[A, B](a => new B)
// mf2 is MemoizedFunction1[B, B]
// mf2 contains mutable.Map[A, B]
mf1.getOtherCache(mf2) //Error! mf2.cache is NOT mutable.Map[B, B]!
Ответ 3
Программирование в Scala затрагивает эту тему в разделе 19.7 Object частные данные: "Доступ к частным частям объекта возможен только из объекта, в котором они определены. Получается, что обращения к переменным из того же объекта, в котором они определены, не создают проблем с дисперсией".