Domanda

Esiste un modo per forzare le classi figlie a sovrascrivere un metodo non astratto di superclasse?

Devo essere in grado di creare istanze della classe genitore, ma se una classe estende questa classe, deve fornire la propria definizione di alcuni metodi.

È stato utile?

Soluzione

Non esiste un modo diretto imposto dal compilatore per farlo, per quanto ne so.

Potresti aggirare il problema non rendendo istanziabile la classe genitore, ma fornendo invece un metodo factory che crea un'istanza di qualche sottoclasse (possibile privata) che ha l'implementazione predefinita:

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
    }
  }
}

Non puoi scrivere new Base() ora, ma puoi utilizzare Base.create() per ottenere l'implementazione predefinita.

Altri suggerimenti

Come altri hanno sottolineato, non puoi farlo direttamente.

Ma un modo per farlo è utilizzare il pattern di strategia , in questo modo:

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
        }
    }
}

Considera l'idea di creare un'interfaccia con questo metodo.I discendenti della classe dovranno implementarlo.

Penso che il modo più semplice sia creare una classe astratta che eredita dalla classe 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, questo è il punto centrale di un metodo astratto.Qual è il tuo caso d'uso?Forse possiamo pensarci in base alla necessità sottostante.

Che ne dici di questo: all'interno dell'implementazione predefinita del metodo, usa la reflection per ottenere l'esatta Classe dell'oggetto.Se la classe non corrisponde esattamente alla tua classe base, lancia un'eccezione RuntimeException o equivalente.

public class Parent {

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

}

C'è un motivo non è possibile!

La classe derivata potrebbe semplicemente chiamare l'implementazione della classe base quando sovrascrive il metodo.

Allora qual è il punto di forzare la classe a sovrascrivere il tuo metodo?Non credo ci sia alcun vantaggio.

La risposta sarebbe no.È possibile riprogettare utilizzando il modello di progettazione del modello.Potrebbe aiutarti.

In alternativa, puoi fare in modo che le tue classi figlie implementino un'interfaccia.L'interfaccia può essere implementata o meno dalla super classe.

Puoi sempre fare in modo che la classe base abbia un metodo che generi un'eccezione.

Tecnicamente la classe base ha definito il metodo, ma non è utilizzabile senza sovrascrivere il metodo.In tal caso, preferisco un'eccezione di runtime poiché non richiedono un'istruzione throws esplicita.Segue un esempio

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 ...
  }

}

Lo svantaggio è che questo non impedisce la creazione di oggetti Base;tuttavia, chiunque tenti di utilizzare uno dei metodi "deve essere sovrascritto" scoprirà presto che avrebbe dovuto sovrascrivere la classe.

Sebbene questa soluzione soddisfi bene la descrizione della richiesta, la tua applicazione probabilmente trarrebbe vantaggio dal non richiedere una soluzione come questa.È molto meglio evitare arresti anomali del runtime con i controlli del compilatore, che è ciò che fornisce la parola chiave abstract.

Rispeccherò le altre risposte e dirò che non esiste un modo imposto dal compilatore per forzare le classi derivate a sovrascrivere un metodo non astratto. Il punto centrale per rendere astratto un metodo è definire che un metodo con questa firma deve esistere, ma non può essere specificato a livello di base e quindi deve essere specificato a un livello derivato. Se esiste un'implementazione funzionante, non banale (poiché non è vuota e non genera solo un'eccezione o mostra un messaggio) di un metodo a livello di base, allora questo non è strettamente necessario affinché il metodo da un consumatore della classe derivata per avere successo. Quindi, il compilatore non deve imporre l'override di un metodo che può essere eseguito con successo sui livelli di base o derivati.

In situazioni in cui si desidera che una classe derivata sovrascriva la propria implementazione funzionante, dovrebbe essere abbastanza ovvio che l'implementazione di base non fa ciò che i consumatori della classe derivata desiderano; la classe base non ha un'implementazione sufficiente o quella sbagliata. In quei casi, devi fidarti che un programmatore derivante dalla tua classe saprà cosa sta facendo e quindi sa che il metodo deve essere sovrascritto perché non produce la risposta giusta nel contesto dell'utilizzo del suo nuovo oggetto.

Mi viene in mente una cosa che potresti fare. Richiederebbe una base astratta, con un'implementazione "predefinita" sigillata (finale per Javaheads). In questo modo, c'è un'implementazione di base del metodo che è prontamente disponibile per l'uso come se fosse una classe "base", ma per definire una classe diversa per un nuovo scenario, è necessario tornare alla classe astratta e sono quindi costretti a reimplementare il metodo. Questo metodo potrebbe essere l'unica cosa astratta sulla classe, permettendoti così di utilizzare le implementazioni di base di altri metodi:

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() { ... }
}

Tuttavia, questo ha due svantaggi. Innanzitutto, poiché non si ha accesso all'implementazione di DefaultClass tramite ereditarietà, non è possibile estendere l'implementazione di DefaultClass, il che significa che per fare ciò che fa DefaultClass, più un po 'di più, è necessario riscrivere il codice da DefaultClass, violando DRY. In secondo luogo, funziona solo per un livello di ereditarietà, perché non è possibile forzare l'override se si consente l'ereditarietà da DerivedClass.

forse questo aiuta:

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, impariamo in questo modo.Aderisco alle linee guida dello stile Java e utilizzo la sintassi Java.Quindi si presume che ereditarietà multipla e modelli C ++ non siano disponibili.

rendere astratto il metodo della classe genitore non è un must, in OOP usi il concetto di polimorfismo.puoi usare lo stesso metodo in due o più modi diversi. Questo è chiamato override del metodo.

facciamo un esempio.

    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(); 
        }
    }

questo stamperà "meowww !!!"sullo schermo.

ma questo non implica che il metodo makeSound debba essere sovrascritto nella classe figlia.

Se è necessario che la classe figlia sia costretta a sovrascrivere i metodi, è meglio implementare un'interfaccia dalla classe.

      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(); 
        }
      }

verrà stampato anche "meowwww !!!"sullo schermo

Potrebbe non essere consigliato, ma puoi lanciare un'eccezione (qualcosa come MethodeMustBeOverRiddenExp) nella tua implementazione del metodo. Ovviamente si tratta di un forzante RunTime, ma potrebbe essere meglio di un pazzo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top