Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Jhaymayleth/unidad2_java/llms.txt

Use this file to discover all available pages before exploring further.

Ejercicio 3 shifts focus from the happy path to a more nuanced question: what happens when an abstract class contains a concrete method that might need to be overridden, but isn’t declared abstract — leaving it up to the subclass to decide whether to customise it? This exercise defines ClaseAbstractaIncorrecta with both an abstract method (metodoAbstracto()) and a concrete default method (metodoConcretoQuePodriaSobreescribirse()), then compares two subclasses: ImplementacionCorrecta, which actively overrides the concrete method, and ImplementacionOlvidadiza, which silently inherits the default. The contrast highlights a real design pitfall that every Java developer should recognise.

Class Hierarchy

ClaseAbstractaIncorrecta  (abstract)
├── ImplementacionCorrecta   — implements abstract method AND overrides concrete method
└── ImplementacionOlvidadiza — implements abstract method but FORGETS to override concrete method

ClaseAbstractaIncorrecta — Abstract Base with a Mixed Contract

The abstract class holds a mensaje field, declares one abstract method, and provides one concrete method with a default behaviour. The name “incorrecta” is intentional: if the concrete method’s behaviour is genuinely expected to vary per subclass, it should be abstract — leaving it concrete is a potential design smell.
package Taller11;

abstract class ClaseAbstractaIncorrecta {
    protected String mensaje;

    public ClaseAbstractaIncorrecta(String mensaje) {
        this.mensaje = mensaje;
    }

    public abstract void metodoAbstracto();

    // Este es un método concreto en una clase abstracta.
    // Podría ser una mala práctica si se espera que las subclases siempre lo modifiquen
    // pero no se declara abstracto.
    public void metodoConcretoQuePodriaSobreescribirse() {
        System.out.println("ClaseAbstractaIncorrecta: Este es un método concreto que hace algo por defecto: " + mensaje);
    }
}

ImplementacionCorrecta — Overrides Both Methods

ImplementacionCorrecta fulfils the abstract contract and also deliberately overrides the concrete method to provide its own specific behaviour. This is the recommended approach when a subclass needs to diverge from the base class default.
class ImplementacionCorrecta extends ClaseAbstractaIncorrecta {
    public ImplementacionCorrecta(String mensaje) {
        super(mensaje);
    }

    @Override
    public void metodoAbstracto() {
        System.out.println("ImplementacionCorrecta: Implementación del método abstracto. Mensaje: " + mensaje);
    }

    // Sobrescribimos el método concreto para darle un comportamiento específico
    @Override
    public void metodoConcretoQuePodriaSobreescribirse() {
        System.out.println("ImplementacionCorrecta: Sobrescribiendo el método concreto. Mensaje: " + mensaje + " (comportamiento específico).");
    }
}

ImplementacionOlvidadiza — Forgets to Override

ImplementacionOlvidadiza implements the mandatory abstract method but never overrides metodoConcretoQuePodriaSobreescribirse(). At runtime it silently falls back to the base class default — a situation that compiles and runs without error, but may produce unexpected output if a specific subclass behaviour was intended.
class ImplementacionOlvidadiza extends ClaseAbstractaIncorrecta {
    public ImplementacionOlvidadiza(String mensaje) {
        super(mensaje);
    }

    @Override
    public void metodoAbstracto() {
        System.out.println("ImplementacionOlvidadiza: Implementación del método abstracto. Mensaje: " + mensaje);
    }
    // Aquí se "olvida" sobrescribir metodoConcretoQuePodriaSobreescribirse()
    // y se hereda el comportamiento por defecto de la clase abstracta, lo cual
    // podría no ser lo deseado.
}

Full MainEjercicio3 Demonstration

package Taller11;

public class MainEjercicio3 {
    public static void main(String[] args) {
        System.out.println("\n--- Demostración de Uso Incorrecto de Clases Abstractas (Ejercicio 3) ---");

        // 1. Intentar instanciar una clase abstracta directamente
        // ClaseAbstractaIncorrecta objAbstracto = new ClaseAbstractaIncorrecta("Mensaje"); // ERROR DE COMPILACIÓN
        System.out.println("1. Intentar instanciar una clase abstracta directamente: \n" +
            "   // ClaseAbstractaIncorrecta objAbstracto = new ClaseAbstractaIncorrecta(\"Mensaje\");\n" +
            "   // Esto generaría un error de compilación: \"ClaseAbstractaIncorrecta is abstract; cannot be instantiated\"");

        System.out.println("\n2. Demostración de método concreto en clase abstracta (posible mala práctica):\n");

        ImplementacionCorrecta correcta = new ImplementacionCorrecta("Implementación Correcta");
        correcta.metodoAbstracto();
        correcta.metodoConcretoQuePodriaSobreescribirse();

        System.out.println("\n");

        ImplementacionOlvidadiza olvidadiza = new ImplementacionOlvidadiza("Implementación Olvidadiza");
        olvidadiza.metodoAbstracto();
        olvidadiza.metodoConcretoQuePodriaSobreescribirse(); // Usa el método de la clase abstracta

        System.out.println("\nExplicación del punto 2:\n" +
            "   - ImplementacionCorrecta sobrescribe el método concreto, lo cual es ideal si se necesita un comportamiento específico.\n" +
            "   - ImplementacionOlvidadiza no sobrescribe el método concreto, por lo que hereda el comportamiento\n" +
            "     por defecto de la clase abstracta. Esto puede ser un error si se esperaba una implementación\n" +
            "     específica y se olvidó sobrescribir. Si el método siempre debe ser implementado por las\n" +
            "     subclases, debería haber sido declarado como abstracto en la clase base.");
    }
}

Expected Output

--- Demostración de Uso Incorrecto de Clases Abstractas (Ejercicio 3) ---
1. Intentar instanciar una clase abstracta directamente:
   // ClaseAbstractaIncorrecta objAbstracto = new ClaseAbstractaIncorrecta("Mensaje");
   // Esto generaría un error de compilación: "ClaseAbstractaIncorrecta is abstract; cannot be instantiated"

2. Demostración de método concreto en clase abstracta (posible mala práctica):

ImplementacionCorrecta: Implementación del método abstracto. Mensaje: Implementación Correcta
ImplementacionCorrecta: Sobrescribiendo el método concreto. Mensaje: Implementación Correcta (comportamiento específico).


ImplementacionOlvidadiza: Implementación del método abstracto. Mensaje: Implementación Olvidadiza
ClaseAbstractaIncorrecta: Este es un método concreto que hace algo por defecto: Implementación Olvidadiza
Notice that the last line comes from ClaseAbstractaIncorrecta, not from ImplementacionOlvidadiza — a silent inheritance that can be hard to spot in larger codebases.

Patterns Demonstrated

PatternClassBehaviour
Cannot instantiate abstract classClaseAbstractaIncorrectaCompile error if new is attempted directly
Must implement abstract methodboth subclassesmetodoAbstracto() must be overridden — no choice
Optional override of concrete methodImplementacionCorrectaActively overrides the default for specific behaviour
Silent default inheritanceImplementacionOlvidadizaFalls back to base class behaviour without warning
If a concrete method in an abstract class is always expected to be customised per subclass, declare it abstract instead. Leaving it concrete gives subclasses the option to override — but the compiler will never warn if they forget.
A useful design heuristic: if you find yourself writing a concrete method in an abstract class and then immediately overriding it in every single subclass, that method should almost certainly be abstract.

Compile & Run

# From the project root — all classes are defined in MainEjercicio3.java
javac Taller11/MainEjercicio3.java

# Run
java Taller11.MainEjercicio3

Build docs developers (and LLMs) love