web-dev-qa-db-fra.com

Existe-t-il un moyen de créer une méthode qui n’est pas abstraite mais qui doit être remplacée?

Existe-t-il un moyen de forcer les classes enfants à remplacer une méthode non abstraite de super classe?

Je dois pouvoir créer des instances de la classe parente, mais si une classe étend cette classe, elle doit donner sa propre définition de certaines méthodes.

53
user517491

Autant que je sache, il n'y a pas de moyen direct imposé par le compilateur de le faire.

Vous pouvez contourner ce problème en non en rendant la classe parente instanciable, mais en fournissant une méthode fabrique qui crée une instance de certaines sous-classes (privées possibles) ayant l'implémentation par défaut:

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

Vous ne pouvez pas écrire new Base() maintenant, mais vous pouvez utiliser Base.create() pour obtenir l’implémentation par défaut.

37
Joachim Sauer

Comme d'autres l'ont souligné, vous ne pouvez pas le faire directement.

Mais une façon de faire est d'utiliser le Modèle de stratégie , comme suit:

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
        }
    }
}
24
Daniel Pryden

Envisagez de créer une interface avec cette méthode. Les descendants de classe devront l'implémenter.

6
Ivan Nikitin

Je pense que le moyen le plus simple est de créer une classe abstraite qui hérite de la classe de 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
    }
}
5
s106mo

Il y a un raison ce n'est pas possible!

La classe dérivée pourrait simplement appeler l'implémentation de la classe de base lors du remplacement de la méthode.

Alors, à quoi ça sert de forcer la classe à remplacer votre méthode? Je pense qu'il n'y a aucun avantage.

3
Mehrdad

Non, c'est tout l'intérêt d'une méthode abstraite. Quel est votre cas d'utilisation? Peut-être pouvons-nous penser à cela en fonction du besoin sous-jacent.

3
mjaggard

Que diriez-vous de cela: dans l’implémentation par défaut de la méthode, utilisez la réflexion pour obtenir la classe exacte de l’objet. Si la classe ne correspond pas exactement à votre classe de base, lancez une exception RuntimeException ou équivalente.

public class Parent {

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

}
3
curioustechizen

La réponse serait un non. Vous pouvez modifier la conception à l'aide d'un modèle de conception de modèle. Cela peut vous aider. 

Vous pouvez également faire en sorte que vos classes enfants implémentent une interface. L'interface peut ou non être implémentée par la super classe.

2
Ravi Bhatt

Vous pouvez toujours faire en sorte que la classe de base ait une méthode qui lève une exception.

Techniquement, la classe de base a défini la méthode, mais celle-ci n'est pas utilisable sans la surcharger. Dans un tel cas, je préfère une exception d'exécution car elle ne nécessite pas d'instruction explicite de projection. Un exemple suit

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

}

L'inconvénient est que cela n'empêche pas la création d'objets Base; Cependant, toute personne tentant d'utiliser l'une des méthodes "doit être remplacée" constatera bientôt qu'elle aurait dû remplacer la classe.

Bien que cette solution réponde bien à la description de la demande, votre application gagnerait probablement à ne pas nécessiter une solution comme celle-ci. Il vaut bien mieux éviter les plantages à l'exécution avec les vérifications du compilateur, ce que fournit le mot clé abstract.

1
Edwin Buck

Je vais refléter les autres réponses et dire qu'il n'y a pas de moyen imposé par le compilateur de forcer les classes dérivées à remplacer une méthode non abstraite. L'intérêt de rendre une méthode abstraite est de définir qu'une méthode avec cette signature doit exister, mais ne peut pas être spécifiée au niveau de base et doit donc être spécifiée à un niveau dérivé. S'il existe une implémentation fonctionnelle et non triviale (car elle n'est pas vide et ne lève pas une exception ou ne montre qu'un message) d'une méthode au niveau de la base, cette opération n'est pas strictement nécessaire pour pouvoir appeler la méthode d'un consommateur de la classe dérivée pour réussir. Le compilateur n'a donc pas à appliquer de redéfinition d'une méthode qui peut s'exécuter avec succès, que ce soit sur la base ou sur les niveaux dérivés. 

Dans les cas où vous voudriez qu'une classe dérivée remplace votre implémentation fonctionnelle, il devrait être assez évident que l'implémentation de base ne fait pas ce que les utilisateurs de la classe dérivée voudront; soit la classe de base n’a pas assez d’implémentation, soit la mauvaise. Dans ces cas, vous devez avoir la certitude qu'un programmeur issu de votre classe saura ce qu'il fait et sait donc que la méthode doit être remplacée, car elle ne fournit pas la bonne réponse dans le contexte de l'utilisation de son nouvel objet.

Je peux penser à une chose que vous pourriez faire. Cela nécessiterait une base abstraite, avec une implémentation "par défaut" scellée (finale pour les Javaheads). De cette manière, il existe une implémentation de base de la méthode qui est facilement utilisable comme une classe "de base", mais pour définir une classe différente pour un nouveau scénario, vous devez revenir à la classe abstraite. sont donc obligés de réimplémenter la méthode. Cette méthode pourrait être la seule chose abstraite sur la classe, vous permettant ainsi de continuer à utiliser les implémentations de base d'autres méthodes:

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

Cependant, cela présente deux inconvénients. Premièrement, étant donné que vous n'avez pas accès à l'implémentation de DefaultClass via l'héritage, vous ne pouvez pas étendre l'implémentation de DefaultClass, ce qui signifie que pour faire ce que DefaultClass fait, vous devez réécrire le code de DefaultClass, en violant DRY. Deuxièmement, cela ne fonctionne que pour un niveau d'héritage, car vous ne pouvez pas forcer le remplacement si vous autorisez l'héritage de DerivedClass.

1
KeithS

peut-être que cela aide:

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
1
Rodrigo

ok, apprenons-le de cette façon. J'adhère aux directives de style Java et j'utilise la syntaxe Java. On suppose donc que plusieurs modèles d'héritage et C++ sont indisponibles.

rendre abstraite la méthode de la classe parente n'est pas une obligation, dans OOP vous utilisez le concept de polymorphisme. vous pouvez utiliser la même méthode de deux manières différentes ou plus. Cette méthode est appelée substitution de méthode.

prenons un exemple.

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

cela va imprimer "meowww !!!" sur l'écran. 

mais cela n'implique pas que la méthode makeSound doit être remplacée dans la classe enfant.

Si vous avez besoin que la classe enfant soit forcée de remplacer les méthodes, vous devez implémenter une interface par la 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(); 
        }
      }

cela affichera également "meowwww !!!" sur l'écran

0
Tharindu Rusira

Cela peut ne pas être recommandé, mais vous pouvez lancer une exception (quelque chose comme MethodeMustBeOverRiddenExp) dans votre implémentation de méthode. Bien sûr, il est forcé par RunTime mais peut être meilleur que noyer.

0
Itay wazana