Не удается преобразовать список в список

#dart

#dart

Вопрос:

 void main() {
  var foo = number as int; // Works
  for (var bar in numbers as List<int>) {} // Run-time error
}

num get number => 0;

List<num> get numbers => [0];
  

Примечание: я не ищу решение, как заставить это работать. Вопрос в том, почему я не могу понизить List<num> до List<int> , когда список на самом деле является типом List<int> .

Комментарии:

1. Почему дженерики ковариантны

Ответ №1:

В этом случае ваш список неявляется List<int> .

Значение списка генерируется

 List<num> get numbers => [0];
  

Вы не указали тип в литерале списка, например <int>[0] , поэтому тип списка будет выведен для вас.
Dart использует две части информации для определения этого типа:

  • Тип контекста (то, что требуется контексту) List<num> . Не у всех выражений есть тип контекста, но у этого есть.
  • Типы элементов (что требуется для элементов), которые int .

Если есть тип контекста, он всегда выигрывает. Таким образом, предполагается, что ваш получатель:

 List<num> get numbers => <num>[0];
  

Когда вы затем пытаетесь сделать numbers as List<int> , это не удается, потому что a List<num> не является List<int> (подтипирование наоборот).

Для других людей, которые действительно ищут решение, вы можете либо преобразовать список в List<int> , создав новый список или обернув его с помощью cast , либо вы можете преобразовать отдельные элементы. Или вы можете принудительно выполнить неявное понижение. Во всех случаях код завершится ошибкой, если список в конечном итоге будет содержать не int значение.

 for (var bar in <int>[...numbers as List<dynamic>]) { ... }
for (var bar in numbers.cast<int>()) { ... }
for (int bar in numbers as List<dynamic>) { ... }
for (var bar in numbers) { ... bar as int ... }
  

Комментарии:

1. Вау, какое это было отличное объяснение! Сэр, в вашем 3-м решении, когда мы делаем numbers as List<dynamic> , эта часть считается улучшенной или пониженной? Все выражение является понижением, которое я знаю.

2. Приведение из numbers в List<dynamic> является приведением вверх. При повторении каждый элемент будет преобразован в int из dynamic . Это потенциально опасно, потому что вместо этого он не поймал бы numbers значение List<String> . В настоящее время я мог бы только что сделать for (int bar in numbers) ... , что неявно понизило бы каждый элемент с num на int , но это перестанет работать, когда будет выпущена функция безопасности Null и все неявные понижения перестанут работать… кроме тех, из dynamic .

3. Понял, спасибо. Но скажите мне одну вещь, где я могу найти столько информации о Dart? Документы не охватывают то, что вы знаете. Единственный способ задать вопрос на SO и дождаться вашего ответа?

4. Этот ответ сочетает в себе языковые возможности ( <int>[...] ), библиотечные возможности ( .cast ) и поведение системы типов (неявные сокращения). Каждый из них документируется индивидуально, либо в документации библиотеки платформы, в сообщении о выпуске языковой функции (или спецификации), либо в пояснении к системе типов Dart 2.0. У Dart нет единого монолитного языка библиотек текста введения к общим пакетам. Возможно, так и должно быть, но это довольно сложная задача.

5. Я вижу ненужное предупреждение о приведении в этой строке numbers as List<dynamic> , хотя вы также не можете его удалить. Должен ли я создать проблему для этого?