Scala верхние границы типа и родительские классы - PullRequest
3 голосов
/ 13 сентября 2011

Мне действительно нравится использовать верхние границы типов, чтобы придать некоторую гибкость тому, что могут принимать мои конструкции. Тем не менее, я действительно не знаю ни одного из принципов, лежащих в основе этого, как я нахожу со следующим кодом:

object BoundsTest {
  abstract trait Service
  class Collection[T <: Service] extends collection.mutable.HashMap[Symbol, collection.mutable.Set[T]] with collection.mutable.MultiMap[Symbol, T]
  type Actives[T <: Service] = collection.mutable.HashMap[Symbol, T]
  class Library[T <: Service](collection: Collection[T], actives: Actives[T])
  private val libraries = new collection.mutable.HashMap[Symbol, Library[Service]]
  def setLibrary[T <: Service](name: Symbol, library: Library[T]) {
    libraries += name -> library
  }
}

Я пытаюсь, чтобы мои классы могли использовать подкласс Service, если он соответствует. Однако это не работает:

$ scalac test.scala 
test.scala:10: error: type mismatch;
 found   : com.bubblefoundry.BoundsTest.Library[T]
 required: com.bubblefoundry.BoundsTest.Library[com.bubblefoundry.BoundsTest.Service]
Note: T <: com.bubblefoundry.BoundsTest.Service, but class Library is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
    libraries += name -> library
                         ^

Проблема, я думаю, в том, как (и когда?) Я определяю libraries, как будто я делаю следующие изменения, все успешно компилируется:

// private val libraries = new collection.mutable.HashMap[Symbol, Library[Service]]
def setLibrary[T <: Service](name: Symbol, library: Library[T]) {
  new collection.mutable.HashMap[Symbol, Library[T]] += name -> library
}

Как я могу объявить libraries HashMap таким образом, чтобы он имел несколько Library с разными Service с? Здесь можно сослаться на Service или это невозможно?

Или я полностью лаю не на том дереве? Спасибо!

Ответы [ 2 ]

9 голосов
/ 13 сентября 2011

В Scala параметризованные типы по умолчанию инвариантны, например, для Cage[A] a Cage[Bird] - это , а не a Cage[Animal]. Но вы можете сделать тип ковариантным или даже контравариантным с помощью объявлений отклонений, например, Cage[+A](a: A), и это то, что компилятор пытается сообщить вам в сообщении об ошибке.

Теперь не всегда возможно сделать параметр типа ковариантным. Это будет работать только в том случае, если переменная типа используется только в так называемых положительных случаях. Или поставить другой (не на 100% правильный) способ, если ваш класс неизменен. В вашем случае это будет работать. Поэтому все, что вам нужно сделать, это добавить + к определению Library:

class Library[+T <: Service](collection: Collection[T], actives: Actives[T])
4 голосов
/ 13 сентября 2011

Как подсказывает ошибка компиляции, класс Library инвариантен.То есть:

Library[S] <: Library[T] если S <: T

не удерживается.Это свойство называется ковариация и является свойством параметров универсального типа.Причина, по которой это вызывает ошибку компилятора, заключается в том, что ваша карта ожидает Library[Service] в качестве типа значения, и вы пытаетесь добавить Library[T] (который из-за отсутствия ковариации не является Library[Service], даже еслиT <: Service)

Если ваш класс Library является неизменным, тогда вы сможете добавить + к параметру типа, чтобы указать scalac, что библиотека является ковариантнойв своем типе.

...