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

🔒 Modificadores de clases

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 #

ModificadorDescrición básicaExemplo
abstractA clase non se pode instanciar. Serve como base para outras clases.abstract class Animal { void mover(); }
baseA clase só pode ser estendida ou implementada dentro da mesma biblioteca.base class A {}
interfacePode ser implementada dende fóra, pero non estendida.interface class B { void saudar(); }
finalA clase non pode ser estendida nin implementada fóra da biblioteca.final class C {}
sealedA clase só pode ser estendida ou implementada dentro da biblioteca actual.sealed class D {}
mixinDefine un mixin (non é un modificador, pero adoita usarse xunto cos demais).mixin E { void falar() => print('Falando'); }
mixin classUnha clase que pode usarse como clase ou como mixin.mixin class F { void andar() => print('Andando'); }
base mixinUn mixin que só pode ser usado dentro da mesma biblioteca.base mixin G {}
final mixinUn mixin que non pode ser implementado nin estendido fóra da biblioteca.final mixin H {}
sealed mixinO mixin só se pode usar dentro da mesma biblioteca de forma exhaustiva.sealed mixin I {}
abstract interfaceDefine unha interface que non se pode instanciar, pero si implementar.abstract interface class X { void metodo(); }
abstract baseCombinación de abstract e base. Non instanciábel, e restrinxida á biblioteca.abstract base class Y {}
abstract mixinMixin 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 que sealed implica abstract).
  • base final: (Equivalente a só final xa que final implica base e interface).

📋 Comparativa de declaracións de clase e mixin #

DeclaraciónInstanciable?Pode herdar?Pode implementar?Usábel como mixin?Exhaustiva en switch?Descrición curta
classClase normal
base classSó herdable na mesma biblioteca
interface classSó pode ser implementada
final classNon herdable nin implementable
sealed classExhaustiva en switch, só herdable na mesma biblioteca
abstract classNon instanciábel, úsase como base
abstract base classAbstracta e só herdable localmente
abstract interface classInterface pura
abstract final classBloquea todas as formas de uso
mixin classClase normal que tamén pode usarse como mixin
base mixin classIgual á anterior, pero só herdable localmente
abstract mixin classNon instanciábel; reusable como mixin
abstract base mixin classVersión restrinxida da anterior
mixinReutilización de comportamento
base mixinMixin limitado á biblioteca local

Exemplos #

🔗 Ver o código en DartPad