diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

View Categories

⚙️ Métodos

En Programación Orientada a Obxectos, os métodos son funcións que pertencen a unha clase é proporcionan o comportamento dun obxecto.

Os métodos permiten interactuar cos atributos e modificar o estado do obxecto.

🔸 Métodos de Instancia (Instance Methods) #

Os métodos de instancia son o tipo máis común de métodos.

Acceden aos atributos do obxecto mediante this (a palabra reservada que fai referencia á propia instancia).

Exemplo simple:

class Persoa {
  String nome;
  int idade;

  Persoa(this.nome, this.idade);

  // Método de instancia que accede aos atributos do obxecto
  void saudar() {
    print('Ola! Son $nome e teño $idade anos.');
  }
}

void main() {
  var p = Persoa('Uxía', 21);
  p.saudar(); // Output: Ola! Son Uxía e teño 21 anos.
}

Outro exemplo:

class ContaBancaria {
  double _saldo; // Atributo privado (convención)

  ContaBancaria(this._saldo); // Construtor

  // Método de instancia para depositar diñeiro
  void depositar(double cantidade) {
    _saldo += cantidade;
    print('Depositados $cantidade€. Novo saldo: ${_saldo}€.');
  }

  // Método de instancia para retirar diñeiro
  double retirar(double cantidade) {
    if (_saldo >= cantidade) {
      _saldo -= cantidade;
      print('Retirados $cantidade€. Novo saldo: ${_saldo}€.');
      return cantidade;
    } else {
      print('Saldo insuficiente. Intentaches retirar $cantidade€, pero só tes ${_saldo}€.');
      return 0.0;
    }
  }

  // Método de instancia para consultar o saldo
  double consultarSaldo() {
    return _saldo;
  }
}

void main() {
  var miConta = ContaBancaria(100.0); // Crear unha instancia de ContaBancaria

  miConta.depositar(50.0);       // Chamar a un método de instancia
  miConta.retirar(30.0);        // Chamar a outro método de instancia
  miConta.retirar(200.0);       // Intento de retirada con saldo insuficiente

  print('Saldo final da conta: ${miConta.consultarSaldo()}€');
}

🔹 Métodos de Clase (Static) #

Os métodos de clase (tamén chamados métodos estáticos) están asociados á clase en si, non a unha instancia particular da clase. Utilízanse cando un método non necesita acceder ás variables de instancia e pode ser chamado directamente usando o nome da clase. Decláranse coa palabra clave static.

class UtilidadesMatematicas {
  // Método estático para calcular o dobre dun número
  static int dobrar(int numero) {
    return numero * 2;
  }

  // Método estático para converter Celsius a Fahrenheit
  static double celsiusToFahrenheit(double celsius) {
    return (celsius * 9 / 5) + 32;
  }
}

void main() {
  // Chamar a métodos estáticos directamente usando o nome da clase
  print('O dobre de 5 é: ${UtilidadesMatematicas.dobrar(5)}');
  print('25 graos Celsius son ${UtilidadesMatematicas.celsiusToFahrenheit(25.0)} graos Fahrenheit.');

  // var obj = UtilidadesMatematicas(); // Non necesitas unha instancia
  // obj.dobrar(10); // ❌ Erro: Non se pode acceder a un método estático dende unha instancia
}

🔹 Métodos con valor de retorno #

Os métodos tamén poden devolver valores, especificando o tipo antes do nome do método:

class Rectangulo {
  double ancho;
  double alto;

  Rectangulo(this.ancho, this.alto);

  // Método que devolve a área do rectángulo
  double area() {
    return ancho * alto;
  }
}

void main() {
  var r = Rectangulo(5, 3);
  print('Área: ${r.area()}'); // Área: 15.0
}

➕ Sobrecarga de Operadores #

Dart permite definir métodos de instancia con nomes especiais que sobrecargan (cambian o comportamento) operadores como
+, -,==, etc.. Isto fai que poidas usar operadores con obxectos da túa clase de forma intuitiva, como se fosen tipos de datos incorporados.

Os operadores que podemos sobrecargar son os seguintes:

OperadorDescrición
<Menor que
>Maior que
<=Menor ou igual que
>=Maior ou igual que
==Igualdade (compara valores)
~Negación bit a bit (bitwise NOT)
-Resta ou operador unario negativo
+Suma
/División (resultado tipo double)
~/División enteira (sen parte decimal)
*Multiplicación
%Módulo (resto da división enteira)
|OU bit a bit (bitwise OR)
ˆXOR bit a bit (bitwise exclusive OR)
&E bit a bit (bitwise AND)
<<Desprazamento de bits á esquerda (bit shift left)
>>>Desprazamento lóxico á dereita (bit shift right sen signo)
>>Desprazamento á dereita con signo (bit shift right conservando signo)
[]Acceso a elementos (ex. lista[0])
[]=Asignación a elementos (ex. mapa['clave'] = valor)

O nome do método é o identificador operator seguido do símbolo do operador.

class Vector {
  final double x;
  final double y;

  const Vector(this.x, this.y);

  // Sobrecarga do operador '+' para sumar dous vectores
  Vector operator +(Vector outro) {
    return Vector(x + outro.x, y + outro.y);
  }

  // Sobrecarga do operador '-' para restar dous vectores
  Vector operator -(Vector outro) {
    return Vector(x - outro.x, y - outro.y);
  }

  // Sobrecarga do operador '==' para comparar dous vectores
  @override // É boa práctica sobreescribir hashCode tamén ao sobrecargar '=='
  bool operator ==(Object outro) {
    if (identical(this, outro)) return true;
    return outro is Vector && outro.x == x && outro.y == y;
  }

  @override
  int get hashCode => x.hashCode ^ y.hashCode; // Necesario con ==
}

void main() {
  final v1 = Vector(2.0, 3.0);
  final v2 = Vector(1.0, 4.0);

  final vSuma = v1 + v2; // Usa o operador '+' sobrecargado
  print('Suma de vectores: (${vSuma.x}, ${vSuma.y})'); // Saída: Suma de vectores: (3.0, 7.0)

  final vResta = v1 - v2; // Usa o operador '-' sobrecargado
  print('Resta de vectores: (${vResta.x}, ${vResta.y})'); // Saída: Resta de vectores: (1.0, -1.0)

  final v3 = Vector(2.0, 3.0);
  print('v1 == v2? ${v1 == v2}'); // Saída: false
  print('v1 == v3? ${v1 == v3}'); // Saída: true (usa o '==' sobrecargado)
}

📥 Getters e Setters #

Os getters e setters son métodos especiais que proporcionan acceso de lectura e escritura ás propiedades dun obxecto. Aínda que cada variable de instancia ten un getter e un setter implícitos (se non é unha variable final), podes definir os teus propios getters e setters explícitos usando as palabras clave get e set.

Son útiles para:

  • Engadir lóxica personalizada ao ler ou escribir unha propiedade (validación, transformación, etc.).
  • Expor unha propiedade de forma controlada sen dar acceso directo á variable de instancia.
  • Calcular un valor dinamicamente ao ser accedido.

📌 NOTA: Dart crea automaticamente un getter para cada atributo e un setter se non é final, pero podemos personalizalos.

class Rectangulo {
  double _lonxitude; // Variábel de instancia privada
  double _largura;   // Variábel de instancia privada

  Rectangulo(this._lonxitude, this._largura);

  // Getter explícito para 'area' (valor calculado)
  double get area {
    return _lonxitude * _largura;
  }

  // Setter explícito para 'lonxitude' (con validación)
  set lonxitude(double novaLonxitude) {
    if (novaLonxitude <= 0) {
      throw ArgumentError('A lonxitude debe ser positiva.');
    }
    _lonxitude = novaLonxitude;
  }

  // Getter explícito para 'lonxitude' (acceso controlado á propiedade privada)
  double get lonxitude => _lonxitude; // Sintaxe abreviada
}

void main() {
  var r = Rectangulo(10.0, 5.0);

  print('Lonxitude inicial: ${r.lonxitude}'); // Accede ao getter 'lonxitude'
  print('Área inicial: ${r.area}');           // Accede ao getter 'area'

  r.lonxitude = 12.0; // Usa o setter 'lonxitude'
  print('Nova lonxitude: ${r.lonxitude}');
  print('Nova área: ${r.area}');

  try {
    r.lonxitude = -2.0; // Intenta asignar un valor inválido
  } catch (e) {
    print('Erro ao establecer lonxitude: $e');
  }
}

🎭 Métodos Abstractos #

Os métodos abstractos son métodos que se declaran nunha clase abstracta pero que non teñen implementación. Simplemente definen unha interface, pero a súa implementación (o seu corpo de código) debe ser proporcionada polas subclases concretas que herdan desa clase abstracta.

Para facer un método abstracto, úsase un punto e coma (;) no canto dun corpo de método.

Exemplo simple:

abstract class Animal {
  void facerSon(); // Método abstracto
}

class Can extends Animal {
  @override
  void facerSon() {
    print('Ladra');
  }
}

Outro exemplo:

// Clase abstracta: non se poden crear instancias dela directamente
abstract class Forma {
  // Método abstracto: non ten implementación
  void debuxar(); // Require implementación nas subclases

  // Método concreto: ten implementación por defecto
  void amosarInformacion() {
    print('Isto é unha forma.');
  }
}

// Subclase concreta que herda de Forma
class Circulo extends Forma {
  double raio;

  Circulo(this.raio);

  // Implementación obrigatoria do método abstracto 'debuxar'
  @override
  void debuxar() {
    print('Debuxando un círculo cun raio de $raio.');
  }
}

class Cadradro extends Forma {
  double lado;

  Cadradro(this.lado);

  // Implementación obrigatoria do método abstracto 'debuxar'
  @override
  void debuxar() {
    print('Debuxando un cadrado cun lado de $lado.');
  }
}

void main() {
  // var forma = Forma(); // ❌ Erro: Non se poden instanciar clases abstractas

  var meuCirculo = Circulo(5.0);
  meuCirculo.debuxar();          // Usa a implementación do Circulo
  meuCirculo.amosarInformacion(); // Usa o método concreto da superclase

  var meuCadrado = Cadradro(4.0);
  meuCadrado.debuxar();          // Usa a implementación do Cadradro
}

📌 Conclusión #

Os métodos son fundamentais para definir o comportamento dos obxectos. Dart permite crear métodos de instancia, sobrescribir operadores, empregar getters e setters personalizados, e definir interfaces mediante métodos abstractos.

🔁 Nos próximos apartados aprenderemos a herdar métodos, sobrescribilos, e organizar mellor o código orientado a obxectos.

Exemplos #

🔗 Ver o código en DartPad