Почему скаляр считает, что HashMap.toArray возвращает Array[A] вместо Array[(A,B)]?
Я смотрел на определение toArray
для хешмапов:
http://www.scala-lang.org/api/current/index.html
Она имеет
toArray: Array[A]
def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]
Я не совсем понимаю - первая часть говорит, что вы получаете массив [A], но вторая часть говорит, что вы получаете массив [B]? Я не ожидаю ничего из этого - Array[(A,B)]
Когда я проверяю это сам:
scala> val x = scala.collection.mutable.HashMap[String, Int]()
x: scala.collection.mutable.HashMap[String,Int] = Map()
scala> x.put("8", 7)
res0: Option[Int] = None
scala> x foreach println
(8,7)
scala> x.toArray
res2: Array[(String, Int)] = Array((8,7))
почему это не похоже на ToList?
toList: scala.List[(A, B)]
3 ответа
В скаладоке есть все тонкие ошибки. Проблема здесь в том, что вы видите "упрощенную" версию сигнатуры метода (предназначенную для передачи основной части сигнатуры и скрытия таких вещей, как CanBuildFrom
в map
/flatMap
методы, которые действительно являются деталями реализации). Упрощение стало немного ошибочным и, похоже, не имеет особого смысла. Если вы нажмете на ссылку "полная подпись", вы увидите, что настоящая подпись выглядит так:
def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]
На самом деле это все еще неправильно, поскольку мы, конечно, не можем иметь тип B, где B >: (A, B). Это должно быть больше похоже на:
def toArray[C >: (A, B)](implicit arg0: ClassTag[C]): Array[C]
Проблема в том, что на самом деле есть два B
s: первый из HashMap
само объявление класса (HashMap[A, +B]
) в то время как другой прибывает из методов toArray
определено в его базовом классе TraversableOnce
(def toArray[B >: A](implicit arg0: ClassTag[B]): Array[B]
). Просто так получилось, что генератору скаладока не удалось дедуплировать два экземпляра B
API, который вы видите в Scaladoc toArray
:
def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]
Эквивалентно:
def toArray[C >: (A, B)](implicit arg0: ClassTag[C]): Array[C]
Выбор типа переменной B
действительно неудачно (и, возможно, даже ошибка в Scaladoc, я не уверен, что вам разрешено это писать).
Это в основном означает, что вы получите массив наиболее определенного супертипа (A,B)
для чего ClassTag
доступно.ClassTag
требуется для того, чтобы создатьArray
,
Это в основном означает, что если во время компиляции, тип времени выполнения Map
вы конвертируете полностью известно, вы получите Array[(A,B)]
, Тем не менее, если вы подняли Map
где-то, тип времени выполнения результирующегоArray
будет зависеть от типа приведения, а не от типа среды выполнения. Это другое поведение, чем toList
и из-за ограничений JVM относительно того, как можно создавать собственные массивы.
Скаладок просто неправ, потому что он наследует toArray
от TraversableOnce
где тип коллекции A
и возвращаемое значение B
, Array[A]
вещь осталась от TraversableOnce
где A
что угодно TraversableOnce
пересекает (в данном случае, на самом деле (A,B)
для другого определения A
а также B
); и хотя он заполняет (A,B)
должным образом в полной форме, он по-прежнему использует B
в качестве новой возвращаемой переменной вместо другой буквы, такой как C
,
Вроде запутанно! Это на самом деле должно читать
def toArray[C >: (A,B)](...[C]): Array[C]
и краткая форма должна быть
toArray: Array[(A,B)]
так же, как вы ожидаете.