Pregunta

¿Hay alguna forma de obligar a las clases secundarias a anular un método no abstracto de superclase?

Necesito poder crear instancias de clase principal, pero si una clase extiende esta clase, debe dar su propia definición de algunos métodos.

¿Fue útil?

Solución

Hasta donde yo sé, no existe una forma directa impuesta por el compilador de hacer esto.

Puede solucionarlo no haciendo que la clase padre sea instanciable, sino proporcionando un método de fábrica que crea una instancia de alguna subclase (posible privada) que tiene la implementación predeterminada:

public abstract class Base {
  public static Base create() {
    return new DefaultBase();
  }

  public abstract void frobnicate();

  static class DefaultBase extends Base {
    public void frobnicate() {
      // default frobnication implementation
    }
  }
}

Ahora no puede escribir new Base(), pero puede hacerlo Base.create() para obtener la implementación predeterminada.

Otros consejos

Como han señalado otros, no puede hacer esto directamente.

Pero una forma de hacerlo es utilizar el patrón de estrategia , así:

public class Base {
    private final Strategy impl;

    // Public factory method uses DefaultStrategy
    // You could also use a public constructor here, but then subclasses would
    // be able to use that public constructor instead of the protected one
    public static Base newInstance() {
        return new Base(new DefaultStrategy());
    }

    // Subclasses must provide a Strategy implementation
    protected Base(Strategy impl) {
        this.impl = impl;
    }

    // Method is final: subclasses can "override" by providing a different
    // implementation of the Strategy interface
    public final void foo() {
        impl.foo();
    }

    // A subclass must provide an object that implements this interface
    public interface Strategy {
        void foo();
    }

    // This implementation is private, so subclasses cannot access it
    // It could also be made protected if you prefer
    private static DefaultStrategy implements Strategy {
        @Override
        public void foo() {
            // Default foo() implementation goes here
        }
    }
}

Considere crear una interfaz con este método.Los descendientes de la clase tendrán que implementarlo.

Creo que la forma más sencilla es crear una clase abstracta que herede de la clase base:


public class Base {
    public void foo() {
        // original method
    }
}

abstract class BaseToInheritFrom extends Base {
    @Override
    public abstract void foo();
}

class RealBaseImpl extends BaseToInheritFrom {
    @Override
    public void foo() {
        // real impl
    }
}

No, ese es el objetivo de un método abstracto.¿Cuál es tu caso de uso?Quizás podamos pensar en ello basándonos en la necesidad subyacente.

¿Qué tal esto? Dentro de la implementación predeterminada del método, use la reflexión para obtener la Clase exacta del objeto.Si la clase no coincide exactamente con tu clase base, lanza una RuntimeException o equivalente.

public class Parent {

    public void defaultImpl(){
        if(this.getClass() != Parent.class){
            throw new RuntimeException();
        }
    }

}

¡Hay una razón por la que no es posible!

La clase derivada podría simplemente llamar la implementación de la clase base al anular el método.

Entonces, ¿cuál es el punto de obligar a la clase a anular su método?No creo que haya ningún beneficio.

La respuesta sería un no.Puede rediseñar utilizando un patrón de diseño de plantilla.Puede que te ayude.

Alternativamente, puede hacer que sus clases secundarias implementen una interfaz.La interfaz puede o no ser implementada por la superclase.

Siempre puede hacer que la clase base tenga un método que genere una excepción.

Técnicamente, la clase base ha definido el método, pero no se puede utilizar sin anular el método.En tal caso, prefiero una excepción de tiempo de ejecución, ya que no requieren una declaración throws explícita.A continuación, se muestra un ejemplo

public class Parent {

  public void doStuff() {
    throw new RuntimeException("doStuff() must be overridden");
  }

}

public class Child extends Parent {

  @Override
  public void doStuff() {
    ... all is well here ...
  }

}

La desventaja es que esto no evita la creación de objetos Base;sin embargo, cualquiera que intente utilizar uno de los métodos "debe ser anulado" pronto encontrará que debería haber anulado la clase.

Si bien esta solución cumple bien la descripción de la solicitud, su aplicación probablemente se beneficiaría de no requerir una solución como esta.Es mucho mejor evitar fallas en el tiempo de ejecución con verificaciones del compilador, que es lo que proporciona la palabra clave abstracta.

Reflejaré las otras respuestas y diré que no hay una forma impuesta por el compilador de forzar a las clases derivadas a anular un método no abstracto. El objetivo de hacer un método abstracto es definir que debe existir un método con esta firma, pero no se puede especificar en el nivel base y, por lo tanto, debe especificarse en un nivel derivado. Si hay una implementación funcional, no trivial (ya que no está vacía y no solo lanza una excepción o muestra un mensaje) de un método en el nivel base, entonces esto no es estrictamente necesario para una llamada a el método de un consumidor de la clase derivada para tener éxito. Por lo tanto, el compilador no tiene que imponer la anulación de un método que puede ejecutarse con bastante éxito en los niveles base o derivado.

En situaciones en las que desee que una clase derivada anule su implementación de trabajo, debería ser bastante obvio que la implementación base no hace lo que los consumidores de la clase derivada querrán; la clase base no tiene suficiente implementación o es incorrecta. En esos casos, debe confiar en que un programador derivado de su clase sabrá lo que está haciendo y, por lo tanto, sabrá que el método debe anularse porque no produce la respuesta correcta en el contexto del uso de su nuevo objeto.

Puedo pensar en una cosa que podrías hacer. Requeriría una base abstracta, con una implementación "predeterminada" sellada (final para los Javaheads). De esa manera, hay una implementación básica del método que está disponible para usar como si fuera una clase "base", pero para definir una clase diferente para un nuevo escenario, debe volver a la clase abstracta y por tanto, se ven obligados a volver a implementar el método. Este método podría ser lo único abstracto en la clase, por lo que aún le permite hacer uso de implementaciones básicas de otros métodos:

public abstract class BaseClass
{
   public abstract void MethodYouMustAlwaysOverride();

   public virtual void MethodWithBasicImplementation() { ... }
}

public final class DefaultClass:BaseClass
{
   public override void MethodYouMustAlwaysOverride() { ... }

   //the base implementation of MethodWithBasicImplementation 
   //doesn't have to be overridden
}

...

public class DerivedClass:BaseClass
{
   //Because DefaultClass is final, we must go back to BaseClass,
   //which means we must reimplement the abstract method
   public override void MethodYouMustAlwaysOverride() { ... }

   //again, we can still use MethodWithBasicImplementation, 
   //or we can extend/override it
   public override void MethodWithBasicImplementation() { ... }
}

Sin embargo, esto tiene dos inconvenientes. Primero, debido a que no tiene acceso a la implementación de DefaultClass a través de la herencia, no puede extender la implementación de DefaultClass, lo que significa que para hacer lo que hace DefaultClass, y un poco más, debe reescribir el código de DefaultClass, violando DRY. En segundo lugar, esto solo funciona para un nivel de herencia, porque no puede forzar la anulación si permite la herencia de DerivedClass.

tal vez esto ayude:

class SuperClass {

    void doStuff(){

        if(!this.getClass().equals(SuperClass.class)){

            throw new RuntimeException("Child class must implement doStuff Method");
        }else{
            //ok
            //default implementation
        }
    }
}

class Child extends SuperClass{

    @Override
    void doStuff() {
        //ok
    }
}

class Child2 extends SuperClass{

}


 new SuperClass().doStuff(); //ok
 new Child().doStuff();         //ok
 new Child2().doStuff();        //error

ok, aprendamos de esta manera.Me adhiero a las pautas de estilo de Java y uso la sintaxis de Java.Por lo tanto, se asume que no se encuentran disponibles varias plantillas de herencia y C ++.

No es obligatorio hacer que el método de la clase principal sea abstracto, en OOP usas el concepto de polimorfismo.puede utilizar el mismo método de dos o más formas diferentes. Esto se llama anulación de método.

tomemos un ejemplo.

    public class Animal{
       public void makeSound(){
          System.out.println("Animal doesn't know how to make sound");
       }

    }

    public class PussyCat extends Animal{
        public void makeSound(){

           System.out.println("meowwww !!!");
        }

        public static void main(String args[]){
           PussyCat aCat=new PussyCat();
           aCat.makeSound(); 
        }
    }

esto imprimirá "¡miau!"en la pantalla.

pero esto no implica que el método makeSound deba anularse en la clase secundaria.

Si necesita que la clase secundaria se vea obligada a anular los métodos, es mejor que implemente una interfaz por la clase.

      public Interface audible{
         public void makeSound();

      }

      public class pussyCat implements audible{
          // now you must implement the body of makeSound method here
          public void makeSound(){
            System.out.println("meowwww !!!"); 
          }

          public static void main(String args[]){
           PussyCat aCat=new PussyCat();
           aCat.makeSound(); 
        }
      }

esto también imprimirá "¡miauwww!"en la pantalla

Puede que no sea recomendable, pero puede lanzar una excepción (algo como MethodeMustBeOverRiddenExp) en la implementación de su método. Por supuesto, es RunTime forzado, pero puede ser mejor que tonterías.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top