# Genéricos no Dart/Flutter: Arranhando a superfície 🌶

Um tipo genérico é basicamente um modelo de dado que pode receber outros tipos de dados sem se importar com seu tipo. 

O Dart é por padrão uma linguagem `type safe`, no final sempre é garantido que o dado seja adequado ao seu tipo, aqui que entram os tipos genéricos garantindo que você possa ter um código reutilizável com menor duplicação ou segurança na tipagem caso precise.

>  Tudo no Dart vem de object mas nem todo `Object` recebe outro `Object`. 
>  `Dynamic` é tudo desde que se descubra quem ele é.
>  P*t* merd4... 🤣

# Object 🤸‍♂️

Não é bem um tipo genérico mas a classe Odin -> `pai de todos`. 

A classe Object é uma superclasse onde tudo no Dart deriva(menos null), em muitos casos usa-se Object para inferir que um tipo genérico não possa ser `null`.

```dart
  class Catiorro<T extends Object> {} // T não pode ser null
  class Bichano<T> {} // T pode ser null
```
Para fazer alguma operação em um tipo Object que referencia um outro tipo, só se dá através de casting.

```dart
  Object inteiro = 1;
  inteiro = inteiro + 1; // aqui temos um erro;
  inteiro = (inteiro as int) + 1; // já aqui não chora na neneca;
  print(inteiro.runtimeType.toString()); // por coincidência em tempo de execução é int, legal? 🧐
```

A classe Object quase não tem membros em sua composição e o casting deve ser obrigatório como por exemplo no codiguinho acima.


# dynamic 🦸

Já o nosso amigo `dynamic` não esquenta com nada, mas em alguns casos pode se tornar menos seguro por que ele não reflete tudo.

Por padrão genéricos são de fato dynamics, então em um `Catiorro<K, V>` K e V são dynamics a não ser que tenha alguma inferência `Catiorro<K extends Neneca, V extends String>`

```dart
  class Neneca{}
  class Catiorro<K extends Neneca, V extends String>{}
```

```dart
   dynamic inteiro = 1;
   inteiro = inteiro + 1;
   inteiro.chorandoNaNeneca(); // nem tudo são flores
```

O Object te força ao casting inevitável já o dynamic não esquenta com a cabeça e por isso é recomendável que se analise o uso em seu código - Forçar o casting ou não? Sempre vai depender das suas abordagens

# Velocidade ⌛

Em algumas medições usando o package [benchmark_harness](https://pub.dev/packages/benchmark_harness) fica evidente uma vantagem do `dynamic` para dados em listas.

```dart
import 'package:benchmark_harness/benchmark_harness.dart';

class ListBenchmark extends BenchmarkBase {
  final String listName;
  final dynamic list;
  const ListBenchmark(this.list, this.listName) : super(listName);

  static void main(dynamic list, String listName) {
    ListBenchmark(list, listName).report();
  }

  @override
  void run() {
    list.sort();
  }

  @override
  void setup() {}

  @override
  void teardown() {}

  @override
  void exercise() => run();
}

void main() {
  final listDynamic = <dynamic>[5, 9, 1, 4, -11, 10, 300, 3, 4, 5, 9, 1, 4, -11, 10, 300, 3, 4, 5, 9, 1, 4, -11, 10, 300, 3, 4];
  final listInt = <int>[5, 9, 1, 4, -11, 10, 300, 3, 4, 5, 9, 1, 4, -11, 10, 300, 3, 4, 5, 9, 1, 4, -11, 10, 300, 3, 4];
  final listObject = <Object>[5, 9, 1, 4, -11, 10, 300, 3, 4, 5, 9, 1, 4, -11, 10, 300, 3, 4, 5, 9, 1, 4, -11, 10, 300, 3, 4];
  // Run Benchmark
  ListBenchmark.main(listDynamic, 'Lista com dynamic');
  ListBenchmark.main(listInt, 'Lista tipada com int');
  ListBenchmark.main(listObject, 'Lista tipada com Object');
}

```

Resultou em algo que talvez te deixe confuso mas que não tem impacto real dependendo da sua aplicação.

```bash
Lista com dynamic(RunTime): 0.29372134156476754 us.
Lista tipada com int(RunTime): 0.3137372864007163 us.
Lista tipada com Object(RunTime): 0.3100669393288707 us.
Exited
```
> `*µs => microsegundos`

# Legalzinho de ver 🌶

Meu amigo [kmartins.dev](https://kmartins.dev/) pediu uma ajuda para lançar uma exceção caso um campo seja null sem precisar usar o `Null assertion operator`, parecido com o `getOrThrow do Kotlin`.

Em conjunto chegamos em uma solução usando genéricos:

```dart
  X getOrThrow<X>(X? x) => x ?? (throw Exception()); 🤔
```

Esquisito ou não? 😵‍💫

Com isso o [kmartins.dev](https://kmartins.dev/) chegou na seguinte extension para Dart/Flutter

```dart
extension ObjectExt<T> on T?{
  T getOrThrow() => this ?? (throw Exception());
  T? getOrNull() => this;
}
```

 Com isso temos:

```dart
  final int? valor = 2;
  final receber= valor.getOrThrow();
  final naoReceber = valor.getOrNull();
```

Como diria o Xaropinho, `rapaaaaaaaaaaizzzzzzzzz` 🐀

%[https://media.giphy.com/media/TgMy2CyKh8y9Na4fHz/giphy.gif]

**(Dá like ae!!!!!)**

#### Fontes

- https://dart.dev/guides/language/language-tour#generics
- https://dart.dev/guides/language/type-system
