Métodos abstractos
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 44 de 110.
Un método abstracto es un método declarado dentro de una clase abstracta que no tiene cuerpo. Define la firma del método pero deja la implementación a cada subclase. Cualquier clase que extienda la clase abstracta debe sobrescribir todos los métodos abstractos, o también será abstracta.
abstract class Animal {
// Método abstracto - sin cuerpo, debe ser implementado por las subclases
String makeSound();
// Método concreto - tiene un cuerpo, compartido por todas las subclases
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
@override
String makeSound() {
return 'Woof!'; // Debe proporcionar una implementación
}
}
class Cat extends Animal {
@override
String makeSound() {
return 'Meow!'; // Implementación diferente, misma firma
}
}Si una subclase olvida implementar un método abstracto, Dart te dará un error de compilación. Este es el punto principal: los métodos abstractos imponen un contrato. Se garantiza que cada subclase tenga ese método, por lo que puedes llamarlo de forma segura en cualquier instancia del tipo abstracto.
// Puedes usar el tipo abstracto como un tipo de variable
Animal a = Dog();
print(a.makeSound()); // ¡Guau!
Animal b = Cat();
print(b.makeSound()); // ¡Miau!Los métodos abstractos son diferentes de los métodos concretos en una clase abstracta. Los métodos concretos tienen un cuerpo y se heredan tal cual. Los métodos abstractos no tienen cuerpo y deben ser sobrescritos.
Puedes mezclar ambos en la misma clase abstracta para compartir cierto comportamiento mientras obligas a las subclases a definir el suyo propio para otros.
| Característica | Método abstracto | Método concreto |
|---|---|---|
| Tiene cuerpo | No | Sí |
| Debe sobrescribirse | Sí | No |
| Propósito | Imponer contrato | Compartir comportamiento |
| Dónde | Solo en clase abstracta | Cualquier clase |
Desafío
FácilVamos a construir una calculadora de formas que demuestre los métodos abstractos en acción. Definirás una clase abstracta con métodos abstractos que cada forma debe implementar de manera diferente, y luego usarás esas formas a través de una interfaz común.
Organizarás tu código en cuatro archivos:
shape.dart: Define una clase abstractaShapecon un campofinalcolor(String) y un constructor que lo inicialice. Declara dos métodos abstractos:double area()ydouble perimeter(). También añade un método concretodescribe()que imprimaA [color] shape with area: [area] and perimeter: [perimeter]. Debido a quearea()yperimeter()son abstractos, cada subclase proporcionará su propio cálculo, perodescribe()funciona para todas ellas sin ser sobrescrito.circle.dart: Crea una claseCircleque extienda deShape. Tiene un campofinalradius(double). Su constructor tomacoloryradiusy pasacolorasuper. Implementaarea()para que devuelva3.14 * radius * radiusyperimeter()para que devuelva2 * 3.14 * radius.rectangle.dart: Crea una claseRectangleque extienda deShape. Tiene camposfinalwidthyheight(ambos double). Su constructor tomacolor,widthyheighty pasacolorasuper. Implementaarea()para que devuelvawidth * heightyperimeter()para que devuelva2 * (width + height).main.dart: Importa todos los archivos. Crea unCirclecon color'red'y radio5.0, y unRectanglecon color'blue', ancho4.0y alto6.0. Llama adescribe()en cada forma. Luego crea unaList<Shape>que contenga ambas formas, recorre la lista e imprimeArea: [area]para cada forma.
Salida esperada:
A red shape with area: 78.5 and perimeter: 31.400000000000002
A blue shape with area: 24.0 and perimeter: 20.0
Area: 78.5
Area: 24.0Hoja de referencia
Un método abstracto es un método declarado en una clase abstracta sin cuerpo. Las subclases deben sobrescribirlo para proporcionar su propia implementación.
abstract class Animal {
// Método abstracto - sin cuerpo
String makeSound();
// Método concreto - tiene cuerpo
void breathe() {
print('Breathing...');
}
}Las subclases deben implementar todos los métodos abstractos:
class Dog extends Animal {
@override
String makeSound() {
return 'Woof!';
}
}
class Cat extends Animal {
@override
String makeSound() {
return 'Meow!';
}
}Puedes usar el tipo abstracto como un tipo de variable:
Animal a = Dog();
print(a.makeSound()); // Woof!
Animal b = Cat();
print(b.makeSound()); // Meow!| Característica | Método Abstracto | Método Concreto |
|---|---|---|
| Tiene cuerpo | No | Sí |
| Debe sobrescribirse | Sí | No |
| Propósito | Imponer contrato | Compartir comportamiento |
| Dónde | Solo en clase abstracta | Cualquier clase |
Pruébalo tú mismo
import 'shape.dart';
import 'circle.dart';
import 'rectangle.dart';
void main() {
// TODO: Crea un Circle con color 'red' y radio 5.0
// TODO: Crea un Rectangle con color 'blue', ancho 4.0 y alto 6.0
// TODO: Llama a describe() en el círculo
// TODO: Llama a describe() en el rectángulo
// TODO: Crea una List<Shape> que contenga ambas formas
// TODO: Recorre la lista e imprime "Area: [area]" para cada forma
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository