Я знаю, это меня тоже достало :)

В группе Reddit / r / dartlang человек по имени NFC_TagsForDroid связался со мной по поводу путаницы при навигации по документации Dart. Это было особенно важно при понимании значения некоторых токенов, используемых при демонстрации примеров кода.

Вот выдержка из комментария пользователя:

Не могли бы вы написать объяснение того, как читать документацию по dartlang? Большая часть этого бессмысленна для новичка. Например: https://api.dartlang.org/stable/2.1.0/dart-core/List/add.html (Dart dart: core List ‹E› добавить абстрактный метод)
заголовок List ‹E› добавить абстрактный метод что такое ‹E›? как это абстрактный метод на что-нибудь влияет?

Пользователь имеет в виду подпись для метода add() типа List:

void add(
  E value
);

Источник путаницы - E. Другие, используемые в различных примерах кода документации, - это T, K и V.

Так что это значит?

Эти, казалось бы, волшебные «помеченные буквы» являются заполнителями, которые используются для представления того, что известно как параметр типа. Это характерно для статически типизированных объектно-ориентированных языков и станет очевидным, когда дело доходит до темы Generics.

Обобщения предоставляют способ сообщить компилятору, какой тип используется, чтобы он знал, что нужно проверить.

Другими словами, если вы видите <E>, прочтите его как «of Тип», значит, List<String> будет прочитан как «Список String ».

Теперь, учитывая это, допустим, мы определяем List<E>:

List<String> fruits = ['apple', 'orange', 'pineapple'];

Снова посмотрим на метод add():

void add(
  E value
);

Это можно понять так: E представляет собой элемент в коллекции, какой бы тип мы ни изначально указали при создании этого списка! В случае fruits это String.

И вот как мы будем его использовать:

fruits.add('mango');
fruits.add(1); // This will throw an error as its the wrong type

Итак, почему используются определенные буквы?

Самый простой ответ - соглашение. На самом деле вы можете использовать любые буквы, которые вам нравятся. Любая буква даст тот же эффект, но общие несут семантическое значение:

  • T должен быть Типом
  • E должен быть элементом (List<E>: список элементов)
  • K - это ключ (в Map<K, V>)
  • V - значение (как возвращаемое или отображаемое значение)

Приведенный ниже код будет работать, даже если я не использую буквы-заполнители, указанные выше. Например, см. Этот фрагмент ниже:

Запуск в DartPad.

Это работает, хотя используется заполнитель A. По соглашению T будет использоваться:

class CacheItem<T> {
  CacheItem(T this.itemToCache);

  final itemToCache;

  String get detail => '''
    Cached item: $itemToCache
    The item type is $T
  ''';
}

Обобщения мощны тем, что они позволяют нам повторно использовать один и тот же класс с различными типами:

CacheItem<String> cachedString = CacheItem<String>('Hello, World!');
print(cachedString.detail);
// Output:
// Cached item: Hello, World!
// The item's type is String

var cachedInt = CacheItem<int>(30);
print(cachedInt.detail);
// Output:
// Cached item: 30
// The item's type is int

var cachedBool = CacheItem<bool>(true);
print(cachedBool.detail);
// Output:
// Cached item: true
// The item's type is bool

Заключение

Я надеюсь, что это было проницательно, и сегодня вы узнали что-то новое.

Я веду канал на YouTube, где обучаю подписчиков разрабатывать полнофункциональные приложения с помощью Dart. Я запланировал веб-серию под названием React.js – Начало работы в Dart, которую нужно загрузить в новогодний день.

Подпишитесь сейчас, чтобы получать уведомление, когда это будет выпущено.

Ставьте лайки, делитесь и подписывайтесь на меня 😍, чтобы узнать больше о Dart.

дальнейшее чтение