Dart 03 – Generics no Dart
O Dart possui uma forma de codificar uma classe ou função para que ela funcione com uma variedade de tipos diferentes, por meio da adoção de Generics. Com o Generics é possível declarar uma classe ou função sem abrir mão da segurança do tipo, que ocorre ao empregar o tipo dynamic. Neste texto vou discutir um pouco sobre como empregar o Generic para criação de classes e funções.
3.1 – Criando Classes e Métodos Generics
A ideia aqui é criar um classe para gerar uma matriz que pode ser de qualquer tipo, ou seja, inteiro, string, double, … Para isto será empregado o uso de Generics para a criação da classe e de alguns métodos desta.
A classe Matrix terá três atributos sendo eles:
rowsecolumns– duas constantes inteiras para armazenar o número de linhas e colunas da matriz;_matrix– uma lista de listas de tipoGenerics.
A estratégia do Dart para declarar uma classe de tipo genérico é empregar a sintaxe “<Letra>” como declaração de seu tipo. A Letra é uma letra qualquer em maiúsculo, por convenção. Algo como:
class Matrix<E> {
final rows;
final columns;
final List<List<E>> _matrix = [];
}
Declara uma classe Matrix que pode ser iniciada com o tipo E que pode ser um int, double, String, ou qualquer outro objeto que se deseje empregar na matriz.
Se desejado é possível estender o domínio da E para o grupo de objetos específicos com um extends
class Matrix<E extends Object> {
final rows;
final columns;
final List<List<E>> _matrix = [];
}
Naturalmente o extends Object aqui não adiciona muita restrição, visto que no Dart praticamente tudo estende um Object, mas vou deixá-lo aí a fim de explicitar a declaração.
Todos os atributos são imutáveis e por isto os declarei como final. O construtor padrão é declarado a seguir, iniciando as linhas de Matrix para o número em rows:
Matrix(this.rows, this.columns) {
for (int r = 0; r < rows; r++) {
_matrix.add(<E>[]);
}
}
Embora seja redundante a declaração da linha do tipo <E> adicionada em _matrix, a deixo explícita pelos mesmos motivos anteriores. Esta inicialização preenche a matriz com rows linhas to tipo <E>[], listas do tipo E. Esta inicialização simplificar o método add, apresentado a seguir.
Para simplificar o preenchimento da matriz vou adicionar um método add para adicionar seus elementos serialmente, linha por linha. O método é bem simples, ele apenas irá verificar o comprimento de cada linha e adicionar o elemento passado na próxima linha incompleta.
add(E element) {
int row = 0;
while (_matrix[row].length == columns && row != rows) {
row++;
}
if (row == rows) return;
_matrix[row].add(element);
}
O elemento a ser adicionado à matriz tem de se do tipo E, (E element). Este é adicionado à linha incompleta ao final do método.
O próximo passo é criar um método para retornar um dos elementos de Matrix<E> de uma dada linha x coluna:
E item(int row, int column) {
if (row >= rows || column >= columns) {
throw Exception('RangeError (index): Invalid value: Not in inclusive'
' range [0..$columns][0..$rows]: [$row][$column]');
}
return _matrix[row][column];
}
O E a frente do nome do método representa o seu tipo de retorno. Neste método tratei apenas o acesso a elementos fora do range rows x columns da matriz, erro por acesso a elementos ainda não adicionado em _matrix serão gerado no próprio acesso à linha do return.
Adicionei mais três métodos sendo eles:
currentLength– para retornar o número total de elementos adicionados a matriz;lenght– um getter para retornar o comprimento máximo da matriz;toString– para formatar uma saída mais instrutiva da matriz.
O código completo da classe Matrix é apresentado a seguir:
class Matrix<E extends Object> {
final int rows;
final int columns;
final List<List<E>> _matrix = [];
Matrix(this.rows, this.columns) {
for (int r = 0; r < rows; r++) {
_matrix.add(<E>[]);
}
}
add(E element) {
int row = 0;
while (_matrix[row].length == columns && row != rows) {
row++;
}
if (row == rows) return;
_matrix[row].add(element);
}
E item(int row, int column) {
if (row >= rows || column >= columns) {
throw Exception('RangeError (index): Invalid value: Not in inclusive'
' range [0..$columns][0..$rows]: [$row][$column]');
}
return _matrix[row][column];
}
int get currentLength {
int count = 0;
for (var row in _matrix) {
count += row.length;
}
return count;
}
int get length => rows * columns;
@override
String toString() {
String strOut = '';
for (List<E> line in _matrix) {
strOut += '${line.toString()}\n';
}
return strOut.trim();
}
}
Testes e Conclusões Finais
Um código para testes é apresentado a seguir:
import 'dart:math';
import 'modules/matrix.dart';
Matrix<int> matrixIntRandom(int r, int c, int maxN) {
Matrix<int> matrix = Matrix<int>(r, c);
for (int count = 0; count < r * c; count++) {
matrix.add(Random().nextInt(maxN));
}
return matrix;
}
Matrix<String> matrixStrRandom(int r, int c, String letters) {
Matrix<String> matrix = Matrix<String>(r, c);
for (int count = 0; count < r * c; count++) {
matrix.add(letters[Random().nextInt(letters.length)]);
}
return matrix;
}
void main() {
Matrix<int> m = matrixIntRandom(2, 3, 100);
print('');
print(m);
print('m.length: ${m.length}');
print('m[1][1]: ${m.item(1, 1)}');
Matrix<String> m2 = matrixStrRandom(5, 4, 'ABCDEFGHIJKLMNOPQRSTUVWXZ');
print('');
print(m2);
print('m2.length: ${m2.length}'); // print(m.item(1, 1));
print('m2[2][2]: ${m2.item(2, 2)}');
// print('m2[5][5]: ${m2.item(5, 5)}');
}
Neste código são implementadas duas funções, sendo a primeira para gerar uma matriz randômica de inteiros, matrixIntRandom nas linhas 5 à 13, e a segunda para gerar uma matriz randômica com as letras passadas por uma string, matrixStrRandom nas linhas 15 à 23.
O código ainda possui algumas exceções a serem tratadas, mas não é o objeto deste texto. A classe Matrix mostra de forma simples como se pode implementar uma classe e métodos de um tipo genérico, sem abrir mão da segurança do emprego de tipos declarados em pró de variáveis dinâmicas.
Observe que o tipo de elemento armazenado na classe Matrix é passada explicitando o seu tipo entre <>, como nas linhas 6 e 16 do código acima. Uma saída típica deste código é apresentada no quadro abaixo:
[73, 37, 59]
[65, 83, 1]
m.length: 6
m[1][1]: 83
[V, L, F, L]
[W, Q, E, F]
[T, C, F, I]
[O, G, T, W]
[P, B, E, S]
m2.length: 20
m2[2][2]: F
Deixe uma resposta