Meu aprendizado em Dart! Introdução: parte 3!
Antes de começar a ler…
A ideia inicial era criar um artigo/tutorial, que contivesse os conhecimentos basico de Dart, e também algumas aplicações no final, mas, acabei percebendo que começou a ficar muito extenso, e por esse motivo decidi dividir em partes, essa primeira em até cinco partes ao longo da semana, essa sendo a terceira parte. Originalmente com mais de 80000(oitenta mil) caracteres para esse texto...
E como eu sempre falo, leia a documentação oficial, se for possível para você!
Até o atual momento da publicação deste texto, o TabNews, não formata o código em Dart corretamente, peço de antemão desculpas por qualquer inconveniência causada pela formatação incorreta do código.
Requisitos
Ter lido as primeiras partes do artigo/tutorial:
Meu aprendizado em Dart! Introdução: parte 1!
Meu aprendizado em Dart! Introdução: parte 2!
Valores de retorno
Todas as funções retornam um valor. Se nenhum valor de retorno for especificado, a instrução return null
; É anexada implicitamente ao corpo da função.
foo() {}
assert(foo() == null);
Operadores
Dart suporta os operadores mostrados na tabela a seguir. A tabela mostra a associatividade do operador do Dart e a precedência do operador do maior para o menor, que são uma aproximação das relações do operador do Dart. Você pode implementar muitos desses operadores como membros de classe.
Description | Operator | Associativity |
---|---|---|
unary postfix | expr++ expr-- () [] ?[] . ?. ! | None |
unary prefix | -expr !expr ~expr ++expr --expr await expr | None |
multiplicative | * / % ~/ | Left |
additive | + - | Left |
shift | << >> >>> | Left |
bitwise AND | & | Left |
bitwise XOR | ^ | Left |
bitwise OR | | | Left |
relational and type test | >= > <= < as is is! | None |
equality | == != | None |
logical AND | && | Left |
logical OR | || | Left |
if null | ?? | Left |
conditional | expr1 ? expr2 : expr3 | Right |
cascade | .. ?.. | Right |
assignment | = *= /= += -= &= ^= etc. | Right |
Aviso: A tabela anterior deve ser usada apenas como um guia útil. A noção de precedência de operador e associatividade é uma aproximação da verdade encontrada na gramática da linguagem. Você pode encontrar o comportamento autoritativo dos relacionamentos de operador do Dart na gramática definida na especificação da linguagem Dart.
Ao usar operadores, você cria expressões. Aqui estão alguns exemplos de expressões de operador:
a++
a + b
a = b
a == b
c ? a : b
a is T
Na tabela de operadores, cada operador tem precedência maior do que os operadores nas linhas que o seguem. Por exemplo, o operador multiplicativo %
tem precedência maior que (e, portanto, é executado antes) o operador de igualdade ==
, que tem precedência maior que o operador AND
lógico &&
. Essa precedência significa que as duas linhas de código a seguir são executadas da mesma maneira:
// Parentheses improve readability.
if ((n % i == 0) && (d % i == 0)) ...
// Harder to read, but equivalent.
if (n % i == 0 && d % i == 0) ...
Aviso: Para operadores que usam dois operandos, o operando mais à esquerda determina qual método é usado. Por exemplo, se você tiver um objeto
Vector
e um objetoPoint
, entãoaVector + aPoint
usa adição de vetor (+
).
Declarações de fluxo de controle
Você pode controlar o fluxo do seu código Dart usando qualquer um dos seguintes:
if
eelse
for
loopswhile
edo-while
loopsbreak
econtinue
switch
ecase
assert
- Durante o desenvolvimento, use uma instrução assert—
assert(condição, optionalMessage)
; —para interromper a execução normal se uma condição booleana for falsa.
- Durante o desenvolvimento, use uma instrução assert—
Você também pode afetar o fluxo de controle usando try-catch
e throw
.
Exceções
Seu código Dart pode lançar e capturar exceções. Exceções são erros que indicam que algo isolate
aconteceu. Se a exceção não for capturada, o isolate
que gerou a exceção é suspenso e, normalmente, o isolate
e seu programa são finalizados.
Classes
Dart é uma linguagem orientada a objetos com classes e herança baseada em mixing
. Todo objeto é uma instância de uma classe e todas as classes, exceto Null
, descendem de Object
. A herança baseada em mixin significa que, embora cada classe (exceto a classe principal, Object?
) tenha exatamente uma superclasse
, o corpo de uma classe pode ser reutilizado em várias hierarquias de classes. Os métodos de extensão são uma maneira de adicionar funcionalidade a uma classe sem alterar a classe ou criar uma subclasse.
Usando membros da classe
Os objetos têm membros que consistem em funções e dados (métodos e variáveis de instância, respectivamente). Quando você chama um método, você o invoca em um objeto: O método tem acesso às funções e aos dados desse objeto.
Use um ponto (.
) para se referir a uma variável de instância ou método:
var p = Point(2, 2);
// Get the value of y.
assert(p.y == 2);
// Invoke distanceTo() on p.
double distance = p.distanceTo(Point(4, 4));
Usar ?.
em vez de .
para evitar uma exceção quando o operando mais à esquerda for nulo:
// If p is non-null, set a variable equal to its y value.
var a = p?.y;
Usando construtores
Você pode criar um objeto usando um construtor. Os nomes dos construtores podem ser ClassName
ou ClassName.identifier
. Por exemplo, o código a seguir cria objetos Point
usando os construtores Point()
e Point.fromJson()
:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
O código a seguir tem o mesmo efeito, mas usa a palavra-chave new
opcional antes do nome do construtor:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
Algumas classes fornecem construtores constantes. Para criar uma constante de tempo de compilação usando um construtor constante, coloque a palavra-chave const
antes do nome do construtor:
var p = const ImmutablePoint(2, 2);
Obtendo o tipo de um objeto
Para obter o tipo de um objeto em tempo de execução, você pode usar a propriedade RuntimeType
do objeto, que retorna um objeto Type
.
print('The type of a is ${a.runtimeType}');
Variáveis de instância
Veja como você declara variáveis de instância:
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
double z = 0; // Declare z, initially 0.
}
Todas as variáveis de instância não inicializadas têm o valor nulo.
Todas as variáveis de instância geram um método getter implícito. Variáveis de instância não finais e variáveis de instância finais atrasadas sem inicializadores também geram um método setter implícito.
Se você iniciar uma variável de instância não atrasada onde ela é declarada, o valor é definido quando a instância é criada, que ocorre antes da execução do construtor e de sua lista de inicializadores.
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
As variáveis de instância podem ser finais, caso em que devem ser definidas exatamente uma vez. Inicialize as variáveis de instância finais e não atrasadas na declaração, usando um parâmetro de construtor ou uma lista de inicializadores do construtor:
class ProfileMark {
final String name;
final DateTime start = DateTime.now();
ProfileMark(this.name);
ProfileMark.unnamed() : name = '';
}
Constructors
Declare um construtor criando uma função com o mesmo nome de sua classe (mais, opcionalmente, um identificador adicional conforme descrito em Construtores nomeados). A forma mais comum de um construtor, o construtor generativo, cria uma nova instância de uma classe:
class Point {
double x = 0;
double y = 0;
Point(double x, double y) {
// See initializing formal parameters for a better way
// to initialize instance variables.
this.x = x;
this.y = y;
}
}
A palavra-chave this
refere-se à instância atual.
Nota: Use o
this
somente quando houver um conflito de nome. Caso contrário, o estilo Dart omite othis
.
Inicializando parâmetros formais
O padrão de atribuir um argumento de construtor a uma variável de instância é tão comum que o Dart iniciou parâmetros formais para facilitar.
A inicialização de parâmetros também pode ser usada para inicializar variáveis de instância não anuláveis ou finais, que devem ser inicializadas ou fornecidas com um valor padrão.
class Point {
final double x;
final double y;
// Sets the x and y instance variables
// before the constructor body runs.
Point(this.x, this.y);
}
As variáveis introduzidas pelos formais de inicialização são implicitamente finais e apenas no escopo da lista inicializadora.
Construtores padrão
Se você não declarar um construtor, um construtor padrão será fornecido para você. O construtor padrão não tem argumentos e invoca o construtor sem argumentos na superclasse.
Construtores não são herdados
Subclasses não herdam construtores de suas superclasses. Uma subclasse que não declara um construtor tem apenas o construtor padrão (sem argumento, sem nome).
Construtores nomeados
Use um construtor nomeado para implementar vários construtores para uma classe ou para fornecer clareza extra:
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
Point(this.x, this.y);
// Named constructor
Point.origin()
: x = xOrigin,
y = yOrigin;
}
Lembre-se de que os construtores não são herdados, o que significa que o construtor nomeado de uma superclasse não é herdado por uma subclasse. Se você deseja que uma subclasse seja criada com um construtor nomeado definido na superclasse, deve implementar esse construtor na subclasse.
Invocando um construtor da superclasse não padrão
Por padrão, um construtor em uma subclasse chama o construtor sem nome e sem argumentos da superclasse. O construtor da superclasse é chamado no início do corpo do construtor. Se uma lista de inicializadores também estiver sendo usada, ela será executada antes da chamada da superclasse. Em resumo, a ordem de execução é a seguinte:
- lista de inicializadores
- construtor sem argumento da superclass
- construtor sem argumento da classe principal
Se a superclasse não tiver um construtor sem nome e sem argumentos, você deverá chamar manualmente um dos construtores da superclasse. Especifique o construtor da superclasse após dois pontos (:
), logo antes do corpo do construtor (se houver).
Aviso: Os argumentos para o construtor da superclasse não têm acesso a isso. Por exemplo, argumentos podem chamar métodos estáticos, mas não métodos de instância.
Para evitar ter que passar manualmente cada parâmetro para a super
invocação de um construtor, você pode usar parâmetros de super inicializador para encaminhar parâmetros para o construtor de superclasse especificado ou padrão. Esse recurso não pode ser usado com construtores de redirecionamento. Os parâmetros do super inicializador têm sintaxe e semântica semelhantes para inicializar parâmetros formais:
class Vector2d {
final double x;
final double y;
Vector2d(this.x, this.y);
}
class Vector3d extends Vector2d {
final double z;
// Forward the x and y parameters to the default super constructor like:
// Vector3d(final double x, final double y, this.z) : super(x, y);
Vector3d(super.x, super.y, this.z);
}
Os parâmetros do super
inicializador não podem ser posicionais se a invocação do super
construtor já tiver argumentos posicionais, mas sempre podem ser nomeados:
class Vector2d {
// ...
Vector2d.named({required this.x, required this.y});
}
class Vector3d extends Vector2d {
// ...
// Forward the y parameter to the named super constructor like:
// Vector3d.yzPlane({required double y, required this.z})
// : super.named(x: 0, y: y);
Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}
Nota: O uso de parâmetros do
super
inicializador requer uma versão da linguagem de pelo menos2.17
. Se você estiver usando uma versão da linguagem anterior, deverá passar manualmente todos os parâmetros do super construtor.
Métodos
Métodos são funções que fornecem comportamento para um objeto.
Métodos de instância
Métodos de instância em objetos podem acessar variáveis de instância e this
. O método distanceTo()
no exemplo a seguir é um exemplo de método de instância:
import 'dart:math';
class Point {
final double x;
final double y;
Point(this.x, this.y);
double distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
Operadores
Operadores são métodos de instância com nomes especiais. Dart permite definir operadores com os seguintes nomes:
<
, >
, <=
, >=
, -
, +
, /
, ~/
, *
, %
, |
, ^
, &
, <<
, >>
, >>>
, [ ]
, [ ] =
, ~
, ==
Uma declaração de operador é identificada usando o operador de identificador integrado. O exemplo a seguir define adição vetorial (+
), subtração (-
) e igualdade (==
):
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator + (Vector v) => Vector(x + v.x, y + v.y);
Vector operator - (Vector v) => Vector(x - v.x, y - v.y);
@override
bool operator == (Object other) =>
other is Vector && x == other.x && y == other.y;
@override
int get hashCode => Object.hash(x, y);
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
Getters e setters
Getters e setters são métodos especiais que fornecem acesso de leitura e gravação às propriedades de um objeto. Lembre-se de que cada variável de instância tem um getter implícito, mais um setter, se apropriado. Você pode criar propriedades adicionais implementando getters e setters, usando as palavras-chave get e set:
class Rectangle {
double left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and bottom.
double get right => left + width;
set right(double value) => left = value - width;
double get bottom => top + height;
set bottom(double value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
Nota: Operadores como incremento (
++
) funcionam da maneira esperada, independentemente de um getter ser definido explicitamente ou não. Para evitar efeitos colaterais inesperados, o operador chama o getter exatamente uma vez, salvando seu valor em uma variável temporária.
Métodos abstratos
Os métodos de instância, getter e setter podem ser abstratos, definindo uma interface, mas deixando sua implementação para outras classes. Métodos abstratos só podem existir em classes abstratas.
Para tornar um método abstrato, use um ponto e vírgula (;
) em vez do corpo do método:
abstract class Doer {
// Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}
Classes abstratas
Use o modificador abstract
para definir uma classe abstrata, uma classe que não pode ser instanciada. Classes abstratas são úteis para definir interfaces, muitas vezes com alguma implementação. Se você deseja que sua classe abstrata pareça ser instanciável, defina um construtor de fábrica.
As classes abstratas geralmente têm métodos abstratos. Aqui está um exemplo de declaração de uma classe abstrata que possui um método abstrato:
// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
// Define constructors, fields, methods...
void updateChildren(); // Abstract method.
}
Interfaces implícitas
Cada classe define implicitamente uma interface contendo todos os membros de instância da classe e de quaisquer interfaces que ela implemente. Se você deseja criar uma classe A
que suporte a API
da classe B
sem herdar a implementação de B
, a classe A
deve implementar a interface B
.
Uma classe implementa uma ou mais interfaces declarando em uma cláusula implementada e, em seguida, fornecendo as APIs exigidas pelas interfaces. Por exemplo:
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final String _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Impostor implements Person {
String get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
Aqui está um exemplo de especificação de que uma classe implementa várias interfaces:
class Point implements Comparable, Location {...}
Estendendo uma classe
Use extends
para criar uma subclasse
e super
para se referir à superclasse
:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
Continua No Próximo Episódio...
Fonte: https://dart.dev/