A partir de Dart 3.0, os modificadores de clases permiten un control máis preciso sobre como as clases poden ser estendidas (extended
), implementadas (implemented
) ou mesturadas (mixed in
). Isto axuda a crear APIs máis robustas e a xestionar mellor as xerarquías de clases, definindo claramente as súas intencións de uso.
Os modificadores aplícanse á declaración da clase e afectan á súa visibilidade e ao seu comportamento dentro e fóra da biblioteca onde se definen. Os principais modificadores son base
, interface
, final
, sealed
e abstract
. [cite_start]Se non se especifica ningún modificador, a clase compórtase como unha clase normal e aberta[cite: 85].
Dart ofrece palabras clave especiais para controlar como se poden usar as clases: se poden ser herdadas, instanciadas, implementadas ou estendidas desde fóra da biblioteca onde foron definidas.
Os modificadores sitúanse antes da palabra clave class
(ou mixin
) e algúns só están dispoñibles desde Dart 3.0 en diante.
📋 Táboa resumo de modificadores de clase #
Modificador | Descrición básica | Exemplo |
---|---|---|
abstract | A clase non se pode instanciar. Serve como base para outras clases. | abstract class Animal { void mover(); } |
base | A clase só pode ser estendida ou implementada dentro da mesma biblioteca. | base class A {} |
interface | Pode ser implementada dende fóra, pero non estendida. | interface class B { void saudar(); } |
final | A clase non pode ser estendida nin implementada fóra da biblioteca. | final class C {} |
sealed | A clase só pode ser estendida ou implementada dentro da biblioteca actual. | sealed class D {} |
mixin | Define un mixin (non é un modificador, pero adoita usarse xunto cos demais). | mixin E { void falar() => print('Falando'); } |
mixin class | Unha clase que pode usarse como clase ou como mixin. | mixin class F { void andar() => print('Andando'); } |
base mixin | Un mixin que só pode ser usado dentro da mesma biblioteca. | base mixin G {} |
final mixin | Un mixin que non pode ser implementado nin estendido fóra da biblioteca. | final mixin H {} |
sealed mixin | O mixin só se pode usar dentro da mesma biblioteca de forma exhaustiva. | sealed mixin I {} |
abstract interface | Define unha interface que non se pode instanciar, pero si implementar. | abstract interface class X { void metodo(); } |
abstract base | Combinación de abstract e base . Non instanciábel, e restrinxida á biblioteca. | abstract base class Y {} |
abstract mixin | Mixin abstracto: non se pode instanciar directamente. | abstract mixin Z { void accion(); } |
📌 Nota importante: moitos destes modificadores foron introducidos en Dart 3.0. É necesario que o teu SDK estea actualizado para que os compile correctamente.
🔗 Consulta oficial → Modifiers and class hierarchies en Dart
abstract
#
Unha clase abstract
non pode ser instanciada directamente. Está deseñada para ser estendida por outras clases, proporcionando unha interface ou unha implementación parcial que as subclases deben completar.
// Clase abstracta: Non se pode crear un obxecto 'Forma' directamente
abstract class Forma {
void debuxar(); // Método abstracto que debe ser implementado polas subclases
void mostrarMensaxe() {
print('Esta é unha forma.');
}
}
class Circulo extends Forma {
double raio;
Circulo(this.raio);
@override
void debuxar() {
print('Debuxando un círculo de raio ${raio}.');
}
}
void main() {
// var f = Forma(); // ❌ Erro: Non se poden instanciar clases abstractas.
var c = Circulo(5.0);
c.debuxar();
c.mostrarMensaxe();
}
base
#
Unha clase base
asegura que todas as súas implementacións e as dos seus subtipos son coñecidas dentro da mesma biblioteca. Isto significa que unha clase base só pode ser estendida (e non implementada) dentro da mesma biblioteca. Se unha clase base está noutra biblioteca, non se pode estender nin implementar.
As clases base garanten que calquera cambio na súa implementación non romperá o código fóra da biblioteca.
// lib/xestor_stock.dart
base class Produto {
String nome;
double prezo;
Produto(this.nome, this.prezo);
void imprimirDetalles() {
print('Produto: $nome, Prezo: $prezo€');
}
}
// lib/xestor_stock.dart (dentro da mesma biblioteca)
class Comida extends Produto { // ✅ Válido: estende unha clase 'base' na mesma biblioteca
Comida(String nome, double prezo) : super(nome, prezo);
}
// main.dart (noutra biblioteca)
// import 'package:o_teu_proxecto/xestor_stock.dart';
// class Bebida extends Produto { } // ❌ Erro: Non se pode estender 'Produto' fóra da súa biblioteca.
// class Servizo implements Produto { } // ❌ Erro: Non se pode implementar 'Produto'.
void main() {
var p = Comida('Mazá', 1.5);
p.imprimirDetalles();
}
interface #
Unha interface
define un esqueleto (conxunto de métodos sen implementar) que debe ser implementado por calquera clase que queira cumprilo.
- Non pode ser estendida, só implementada.
- Úsase para indicar que unha clase debe ter certos comportamentos, sen indicar como se fan.
interface class Rexistrable { // Só pode ser implementada
void rexistrarEvento(String evento);
}
class ServizoAnaliticas implements Rexistrable {
@override
void rexistrarEvento(String evento) {
print('Analíticas: Rexístrase o evento "$evento".');
}
}
// class Informe implements Rexistrable { } // ✅ Válido
// class Controlador extends Rexistrable { } // ❌ Erro: Non se pode estender unha clase 'interface'.
void main() {
var analiticas = ServizoAnaliticas();
analiticas.rexistrarEvento('Inicio de sesión');
}
final #
Unha clase final
non pode ser estendida nin implementada. É unha clase que se considera “completa” e non se espera que haxa subtipos. É útil para API nas que queres evitar que o teu código sexa modificado por herdanza, garantindo a súa estabilidade.
final class ConfiguracionApp { // Non se pode estender nin implementar
final String tema;
const ConfiguracionApp(this.tema);
}
// class ConfigCustom extends ConfiguracionApp {} // ❌ Erro: Non se pode estender unha clase 'final'.
// class OutraConfig implements ConfiguracionApp {} // ❌ Erro: Non se pode implementar unha clase 'final'.
void main() {
var config = ConfiguracionApp('Modo Escuro');
print('Tema da app: ${config.tema}');
}
sealed #
Unha clase sealed
é similar a unha clase abstract
, pero con unha restrición adicional: só pode ser estendida ou implementada dentro da mesma biblioteca (arquivo ou conxunto de arquivos na mesma biblioteca) onde se define.
// lib/eventos.dart
sealed class EventoUI { // Só se pode estender/implementar dentro desta mesma biblioteca
// Constructor privado para evitar instanciacións directas e forzar subtipos definidos
const EventoUI();
}
class Clic extends EventoUI { // ✅ Válido: está na mesma biblioteca que EventoUI
final int x, y;
const Clic(this.x, this.y);
}
class DobleClic extends EventoUI { // ✅ Válido: está na mesma biblioteca
final int x, y;
const DobleClic(this.x, this.y);
}
class Deslizar extends EventoUI { // ✅ Válido: está na mesma biblioteca
final String direccion;
const Deslizar(this.direccion);
}
// main.dart (noutra biblioteca)
// import 'package:o_teu_proxecto/eventos.dart';
// class PulsacionLarga extends EventoUI {} // ❌ Erro: Non se pode estender 'EventoUI' fóra da súa biblioteca.
void procesarEvento(EventoUI evento) {
// Con 'sealed', o compilador pode garantir que se manexan todos os posibles subtipos.
// Se engades un novo subtipo de EventoUI e non o manexas aquí, darache un warning.
switch (evento) {
case Clic(x: var x, y: var y): // Usa desestruturación
print('Evento Clic en ($x, $y)');
case DobleClic(x: var x, y: var y):
print('Evento Doble Clic en ($x, $y)');
case Deslizar(direccion: var dir):
print('Evento Deslizar na dirección $dir');
// Non se necesita 'default' se manexas todos os subtipos selados (o compilador sabe cales son)
}
}
void main() {
procesarEvento(Clic(10, 20));
procesarEvento(Deslizar('Arriba'));
}
🧠 Para saber máis… Combinación de Modificadores #
Algúns modificadores pódense combinar. A combinación de modificadores aplícanse as restricións de ambos.
abstract base
: Non se pode instanciar directamente, só se pode estender dentro da mesma biblioteca.abstract interface
: Non se pode instanciar directamente, só se pode implementar (non estender).sealed abstract
: (Equivalentemente sósealed
xa quesealed
implicaabstract
).base final
: (Equivalente a só final xa que final implica base e interface).
📋 Comparativa de declaracións de clase e mixin #
Declaración | Instanciable? | Pode herdar? | Pode implementar? | Usábel como mixin? | Exhaustiva en switch? | Descrición curta |
---|---|---|---|---|---|---|
class | ✅ | ✅ | ✅ | ❌ | ❌ | Clase normal |
base class | ✅ | ✅ | ❌ | ❌ | ❌ | Só herdable na mesma biblioteca |
interface class | ✅ | ❌ | ✅ | ❌ | ❌ | Só pode ser implementada |
final class | ✅ | ❌ | ❌ | ❌ | ❌ | Non herdable nin implementable |
sealed class | ❌ | ❌ | ❌ | ❌ | ✅ | Exhaustiva en switch , só herdable na mesma biblioteca |
abstract class | ❌ | ✅ | ✅ | ❌ | ❌ | Non instanciábel, úsase como base |
abstract base class | ❌ | ✅ | ❌ | ❌ | ❌ | Abstracta e só herdable localmente |
abstract interface class | ❌ | ❌ | ✅ | ❌ | ❌ | Interface pura |
abstract final class | ❌ | ❌ | ❌ | ❌ | ❌ | Bloquea todas as formas de uso |
mixin class | ✅ | ✅ | ✅ | ✅ | ❌ | Clase normal que tamén pode usarse como mixin |
base mixin class | ✅ | ✅ | ❌ | ✅ | ❌ | Igual á anterior, pero só herdable localmente |
abstract mixin class | ❌ | ✅ | ✅ | ✅ | ❌ | Non instanciábel; reusable como mixin |
abstract base mixin class | ❌ | ✅ | ❌ | ✅ | ❌ | Versión restrinxida da anterior |
mixin | ❌ | ❌ | ✅ | ✅ | ❌ | Reutilización de comportamento |
base mixin | ❌ | ❌ | ❌ | ✅ | ❌ | Mixin limitado á biblioteca local |