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:
Operador | Descrició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.