Pular para o conteúdo principal

Generics

Aprenda sobre tipos genéricos em Dart.

Se você olhar a documentação da API para o tipo de array básico, List, verá que o tipo é na verdade List<E>. A notação <...> marca List como um tipo genérico (ou parametrizado)—um tipo que possui parâmetros de tipo formais. Por convenção, a maioria das variáveis de tipo têm nomes de uma única letra, como E, T, S, K e V.

Por que usar generics?

#

Generics são frequentemente necessários para segurança de tipo, mas eles têm mais benefícios do que apenas permitir que seu código execute:

  • Especificar corretamente tipos genéricos resulta em código gerado melhor.
  • Você pode usar generics para reduzir duplicação de código.

Se você pretende que uma lista contenha apenas strings, você pode declará-la como List<String> (leia isso como "lista de string"). Dessa forma você, seus colegas programadores e suas ferramentas podem detectar que atribuir um não-string à lista é provavelmente um erro. Aqui está um exemplo:

análise estática: falha dart
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error

Outra razão para usar generics é reduzir duplicação de código. Generics permitem que você compartilhe uma única interface e implementação entre muitos tipos, enquanto ainda aproveita a análise estática. Por exemplo, digamos que você crie uma interface para fazer cache de um objeto:

dart
abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

Você descobre que deseja uma versão específica para string desta interface, então você cria outra interface:

dart
abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}

Mais tarde, você decide que quer uma versão específica para número desta interface... Você entendeu a ideia.

Tipos genéricos podem evitar o trabalho de criar todas essas interfaces. Em vez disso, você pode criar uma única interface que aceita um parâmetro de tipo:

dart
abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}

Neste código, T é o tipo substituto. É um placeholder que você pode pensar como um tipo que um desenvolvedor definirá mais tarde.

Usando literais de coleção

#

Literais de list, set e map podem ser parametrizados. Literais parametrizados são exatamente como os literais que você já viu, exceto que você adiciona <type> (para listas e sets) ou <keyType, valueType> (para maps) antes do colchete de abertura. Aqui está um exemplo de uso de literais tipados:

dart
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines',
};

Usando tipos parametrizados com construtores

#

Para especificar um ou mais tipos ao usar um construtor, coloque os tipos em colchetes angulares (<...>) logo após o nome da classe. Por exemplo:

dart
var nameSet = Set<String>.of(names);

O código a seguir cria um SplayTreeMap que tem chaves inteiras e valores do tipo View:

dart
var views = SplayTreeMap<int, View>();

Coleções genéricas e os tipos que elas contêm

#

Tipos genéricos Dart são reificados, o que significa que eles carregam suas informações de tipo em tempo de execução. Por exemplo, você pode testar o tipo de uma coleção:

dart
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

Restringindo o tipo parametrizado

#

Ao implementar um tipo genérico, você pode querer limitar os tipos que podem ser fornecidos como argumentos, para que o argumento deva ser um subtipo de um tipo específico. Esta restrição é chamada de bound. Você pode fazer isso usando extends.

Um caso de uso comum é garantir que um tipo seja não-nulo tornando-o um subtipo de Object (em vez do padrão, Object?).

dart
class Foo<T extends Object> {
  // Any type provided to Foo for T must be non-nullable.
}

Você pode usar extends com outros tipos além de Object. Aqui está um exemplo de extensão de SomeBaseClass, para que membros de SomeBaseClass possam ser chamados em objetos do tipo T:

dart
class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}
​
class Extender extends SomeBaseClass {
  ...
}

É correto usar SomeBaseClass ou qualquer um de seus subtipos como argumento genérico:

dart
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

Também é correto não especificar nenhum argumento genérico:

dart
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'

Especificar qualquer tipo não-SomeBaseClass resulta em um erro:

análise estática: falha dart
var foo = Foo<Object>();

Restrições de parâmetro de tipo autorreferencial (F-bounds)

#

Ao usar bounds para restringir tipos de parâmetro, você pode referir o bound de volta ao próprio parâmetro de tipo. Isso cria uma restrição autorreferencial, ou F-bound. Por exemplo:

dart
abstract interface class Comparable<T> {
  int compareTo(T o);
}
​
int compareAndOffset<T extends Comparable<T>>(T t1, T t2) =>
    t1.compareTo(t2) + 1;
​
class A implements Comparable<A> {
  @override
  int compareTo(A other) => /*...implementation...*/ 0;
}
​
int useIt = compareAndOffset(A(), A());

O F-bound T extends Comparable<T> significa que T deve ser comparável a si mesmo. Então, A só pode ser comparado a outras instâncias do mesmo tipo.

Usando métodos genéricos

#

Métodos e funções também permitem argumentos de tipo:

dart
T first<T>(List<T> ts) {
  // Do some initial work or error checking, then...
  T tmp = ts[0];
  // Do some additional checking or processing...
  return tmp;
}

Aqui o parâmetro de tipo genérico em first (<T>) permite que você use o argumento de tipo T em vários lugares:

  • No tipo de retorno da função (T).
  • No tipo de um argumento (List<T>).
  • No tipo de uma variável local (T tmp).

Encontrou essa página sem tradução ou que precisa de correção? Abra uma issue e nos ajude a manter esse site em PT-BR para ajudar pessoas como você.