C'è un modo per creare un metodo che non sia astratto ma che debba essere sovrascritto?
-
27-10-2019 - |
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.
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.