Почему Seq.contains принимает тип Any, а не параметр типа A?

Глядя на контроллер и немного узнавая о том, как работает MVC, я смог понять это.

Мое представление было одним из автоматически сгенерированных и содержало эту строку кода:

@Html.DropDownList("PriorityID", string.Empty)

Чтобы добавить атрибуты html, мне нужно было сделать что-то вроде этого:

@Html.DropDownList("PriorityID", (IEnumerable)ViewBag.PriorityID, new { @class="dropdown" })

Еще раз спасибо @Laurent за вашу помощь, я понимаю, что вопрос wasn 't так ясно, как могло бы быть ...

UPDATE:

Лучшим способом сделать это было бы использовать DropDownListFor , где это возможно, что вы не полагаетесь на магическую строку для атрибута name

@Html.DropDownListFor(x => x.PriorityID, (IEnumerable)ViewBag.PriorityID, new { @class = "dropdown" })

13
задан Community 23 May 2017 в 12:25
поделиться

3 ответа

Проблема в том, что Seq ковариантен по своему параметру типа. Это имеет смысл для большинства его функций. Как неизменяемый контейнер, он действительно должен быть ковариантным. К сожалению, это мешает, когда им нужно определить метод, который принимает некоторые из параметризованных типов. Рассмотрим следующий пример:

trait Seq[+A] {
  def apply(i: Int): A       // perfectly valid

  def contains(v: A): Boolean   // does not compile!
}

Проблема в том, что функции всегда контравариантны по своим типам параметров и ковариантны по своим типам возвращаемых данных. Таким образом, метод apply может возвращать значение типа A , потому что A ковариантен вместе с типом возврата для apply . Однако содержит не может принимать значение типа A , потому что его параметр должен быть контравариантным.

Эту проблему можно решить по-разному. Один из вариантов - просто сделать A параметром инвариантного типа. Это позволяет использовать его как в ковариантном, так и в контравариантном контексте. Однако такая конструкция будет означать, что Seq [String] не будет , а не подтипом Seq [Any] . Другой вариант (и наиболее часто используемый) - использовать параметр локального типа, который ограничен снизу ковариантным типом. Например:

trait Seq[+A] {
  def +[B >: A](v: B): Seq[B]
}

Этот трюк сохраняет свойство Seq [String] <: Seq [Any] , а также обеспечивает некоторые очень интуитивно понятные результаты при написании кода, использующего гетерогенные контейнеры. Например:

val s: Seq[String] = ...
s + 1      // will be of type Seq[Any]

Результатом функции + в этом примере является значение типа Seq [Any] , потому что Any является наименее верхним bound (LUB) для типов String и Int (другими словами, наименее распространенный супертип). Если задуматься, это именно то поведение, которого мы и ожидали. Если вы создаете последовательность с компонентами String и Int , то ее тип должен быть Seq [Any] .

К сожалению, этот трюк, хотя и применим к таким методам, как contains , дает некоторые удивительные результаты:

trait Seq[+A] {
  def contains[B >: A](v: B): Boolean    // compiles just fine
}

val s: Seq[String] = ...
s contains 1        // compiles!

Проблема здесь в том, что мы вызываем метод contains , передавая значение введите Int .Scala видит это и пытается вывести тип для B , который является супертипом как Int , так и A , который в данном случае создается как String . LUB для этих двух типов - Any (как показано ранее), поэтому экземпляр локального типа для содержит будет Any => Boolean . Таким образом, содержит метод кажется небезопасным по типу.

Этот результат не является проблемой для Map или Set , потому что ни один из них не является ковариантным по своим типам параметров:

trait Map[K, +V] {
  def contains(key: K): Boolean    // compiles
}

trait Set[A] {
  def contains(v: A): Boolean      // also compiles
}

Короче говоря, Метод contains для ковариантных типов контейнеров не может быть ограничен приемом только значений типа компонента из-за способа работы типов функций (контравариантных по типам параметров). На самом деле это не ограничение Scala или плохая реализация, это математический факт.

Утешительный приз в том, что на практике это не проблема. И, как упоминалось в других ответах, вы всегда можете определить свое собственное неявное преобразование, которое добавляет «типобезопасный» , содержащий -подобный метод, если вам действительно нужна дополнительная проверка.

31
ответ дан 1 December 2019 в 19:07
поделиться

Я слышал об этом из-за изменений в поле базы данных (например, от даты до отметки времени). Возможно, стоит отменить изменения базы данных, если вы можете их протестировать, или проверить .hbm или аннотации, как предложил Сэндс.

-121--2959439-

Это функция проектировать калиток. Класс можно использовать для связывания стилей и компонентов.

<form wicket:id="form" id="form>

Также можно попробовать (я никогда этого не делал) setMarkupId . Я не уверен, что это хорошо путь.

-121--3238911-

Я не уверен, почему вещи были разработаны таким образом - возможно, чтобы зеркально отразить что-то на Java.

В любом случае, использование образца «сутенер-моя библиотека» более эффективно, чем клонирование в набор:

class SeqWithHas[T](s: Seq[T]) {
  def has(t: T) = s.contains(t)
}
implicit def seq2seqwithhas[T](s: Seq[T]) = new SeqWithHas(s)

scala> List("3","5") has 1
<console>:7: error: type mismatch;
 found   : Int(1)
 required: java.lang.String
       List("3","5") has 1
                         ^

scala> List("3","5") has "1"
res1: Boolean = false

(Вероятно, вы захотите поместить этот материал и другие удобные вещи в один объект, а затем импортировать MyHandyObject._ в большинство ваших исходных файлов)

4
ответ дан 1 December 2019 в 19:07
поделиться

Если вы готовы отказаться от Infix в пользу регулярного вызова метода, определение и импорт следующего имеет (...) способ избежать создания экземпляра каждый раз, когда вам нужен тип Безопасный "имеет" тест (стоит во внутренних циклах, например):

def has[T](s: Set[T], t: T) = s.contains(t)

, естественно, набор [T] может быть расслаблен в наименьший конкретный тип, который имеет содержащий метод.

3
ответ дан 1 December 2019 в 19:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: