201 votes

Héritage des constructeurs Java

Je me demandais pourquoi en java les constructeurs ne sont pas hérités ? Vous savez quand vous avez une classe comme celle-ci :

public class Super {

  public Super(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
    this.serviceA = serviceA;
    //etc
  } 

}

Plus tard, lorsque vous héritez de Super java se plaindra qu'il n'y a pas de constructeur par défaut défini. La solution est évidemment quelque chose comme :

public class Son extends Super{

  public Son(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
    super(serviceA,serviceB,serviceC);
  }

}

Ce code est répétitif, non DRY et inutile (IMHO)... ce qui amène à nouveau la question :

Pourquoi Java ne supporte pas l'héritage des constructeurs ? Y a-t-il un avantage à ne pas autoriser cet héritage ?

3 votes

Je suis d'accord que le constructeur dans Son est répétitif. C'est pour cette raison que le C++ permet à les classes dérivées doivent hériter des constructeurs de base (voir www2.research.att.com/~bs/C++0xFAQ.html#héritage ). Notez que j'insiste sur "permettre" car la classe dérivée doit déclarer explicitement qu'elle utilise les constructeurs de la base.

1 votes

Comme C++, Java bénéficierait également d'une syntaxe permettant l'héritage des constructeurs.

0 votes

Si vous avez un private final int foo; dans la superclasse Super vous ne pouvez pas assigner une valeur à foo dans le constructeur hérité dans Son parce qu'elle est privée dans Super .

220voto

Jon Skeet Points 692016

Supposons que les constructeurs étaient hérité... alors parce que chaque classe finit par dériver de Object, chaque se retrouverait avec un constructeur sans paramètre. C'est une mauvaise idée. Qu'est-ce que vous attendez exactement :

FileInputStream stream = new FileInputStream();

à faire ?

Maintenant, il devrait potentiellement y avoir un moyen de créer facilement les constructeurs "pass-through" qui sont assez communs, mais je ne pense pas que cela devrait être la valeur par défaut. Les paramètres nécessaires pour construire une sous-classe sont souvent différents de ceux requis par la super-classe.

39 votes

Pourquoi pas ? permettre une classe dérivée pour hériter optionnellement des constructeurs de sa base comme le fait C++ (voir www2.research.att.com/~bs/C++0xFAQ.html#inheriting) ?

4 votes

Une syntaxe de constructeur utile pourrait être de permettre à un constructeur dérivé d'hériter des paramètres d'un constructeur de base et de les transmettre automatiquement au constructeur de base afin que le constructeur dérivé n'ait pas à répéter ces paramètres.

6 votes

Qu'est-ce qui vous empêche de déclarer simplement le constructeur sans paramètre comme étant privé si vous n'en avez pas besoin ?

32voto

David Santamaria Points 3181

Lorsque vous héritez de Super, c'est ce qui se passe en réalité :

public class Son extends Super{

  // If you dont declare a constructor of any type, adefault one will appear.
  public Son(){
    // If you dont call any other constructor in the first line a call to super() will be placed instead.
    super();
  }

}

C'est donc la raison, car vous devez appeler votre constructeur unique, puisque "Super" n'en a pas par défaut.

Maintenant, j'essaie de deviner pourquoi Java ne supporte pas l'héritage des constructeurs, probablement parce qu'un constructeur n'a de sens que s'il parle d'instances concrètes, et que l'on ne devrait pas pouvoir créer une instance de quelque chose quand on ne sait pas comment elle est définie (par le polymorphisme).

11voto

gustafc Points 13552

Parce que la construction de l'objet de votre sous-classe peut se faire d'une manière différente de celle de votre super-classe. Vous pouvez ne pas vouloir que les clients de la sous-classe puissent appeler certains constructeurs disponibles dans la super-classe.

Un exemple idiot :

class Super {
    protected final Number value;
    public Super(Number value){
        this.value = value;
    }
}

class Sub {
    public Sub(){ super(Integer.valueOf(0)); }
    void doSomeStuff(){
        // We know this.value is an Integer, so it's safe to cast.
        doSomethingWithAnInteger((Integer)this.value);
    }
}

// Client code:
Sub s = new Sub(Long.valueOf(666L)): // Devilish invocation of Super constructor!
s.doSomeStuff(); // throws ClassCastException

Ou encore plus simple :

class Super {
    private final String msg;
    Super(String msg){
        if (msg == null) throw new NullPointerException();
        this.msg = msg;
    }
}
class Sub {
    private final String detail;
    Sub(String msg, String detail){
        super(msg);
        if (detail == null) throw new NullPointerException();
        this.detail = detail;
    }
    void print(){
        // detail is never null, so this method won't fail
        System.out.println(detail.concat(": ").concat(msg));
    }
}
// Client code:
Sub s = new Sub("message"); // Calling Super constructor - detail is never initialized!
s.print(); // throws NullPointerException

À partir de cet exemple, vous voyez que vous auriez besoin d'un moyen de déclarer que "je veux hériter de ces constructeurs" ou "je veux hériter de tous les constructeurs sauf ceux-là", et ensuite vous devriez également spécifier une préférence d'héritage de constructeur par défaut juste au cas où quelqu'un ajoute un nouveau constructeur dans la superclasse... ou vous pourriez simplement exiger que vous répétiez les constructeurs de la superclasse si vous voulez en "hériter", ce qui est sans doute la façon la plus évidente de le faire.

1 votes

Vos exemples ne me convainquent pas. Ce n'est pas parce que vous codez des bugs à dessein que cela constitue un argument valable contre l'héritage des constructeurs. Les NullPointerExceptions sont tout à fait normales si vous bâclez (et parfois même si vous ne le faites pas).

0 votes

Le point est qu'une chose importante pour laquelle les constructeurs sont utilisés, est de faire respecter les invariants de classe. L'héritage implicite des constructeurs rendrait beaucoup plus compliqué de donner des garanties sur les instances d'une classe (impossible, même, si vous considérez que les constructeurs peuvent être ajoutés aux superclasses).

1 votes

Ce n'est pas un bon exemple. Votre problème est que vous convertissez des nombres non entiers en nombres entiers. Cela n'a rien à voir avec l'héritage.

5voto

Andrzej Doyle Points 52541

Parce que les constructeurs sont un détail d'implémentation - ils ne sont pas quelque chose que l'utilisateur d'une interface/superclasse peut invoquer. Au moment où il obtient une instance, celle-ci a déjà été construite ; et vice-versa, au moment où vous construisez un objet, il n'y a par définition aucune variable à laquelle il est actuellement assigné.

Pensez à ce que cela signifierait de forcer toutes les sous-classes à avoir un constructeur hérité. Je pense qu'il est plus clair de passer les variables directement que de demander à la classe d'avoir "magiquement" un constructeur avec un certain nombre d'arguments juste parce que son parent l'a fait.

2voto

Jonathan Feinberg Points 24791

La réponse de David est correcte. J'aimerais ajouter que vous pourriez recevoir un signe de Dieu vous indiquant que votre conception est défectueuse, et que "Son" ne devrait pas être une sous-classe de "Super", mais qu'au contraire, Super a un détail d'implémentation mieux exprimé par ayant la fonctionnalité que Son fournit, comme une sorte de stratégie.

EDIT : La réponse de Jon Skeet est la plus impressionnante.

0 votes

Super est en réalité un LayerSupertype ( martinfowler.com/eaaCatalog/layerSupertype.html ) de ma couche de service. Et j'ai besoin que les dépendances soient injectées dans le constructeur pour supporter le DI. Ma conception n'est pas perturbé

0 votes

Eh bien, c'est une question d'opinion ! Il me semble que si votre hiérarchie est déformée pour répondre aux exigences de votre cadre de mots à la mode, cela pourrait être en soi un signe du ciel.

0 votes

Veuillez expliquer pourquoi vous dites que ma hiérarchie est déformé . Et je n'ai jamais mentionné de framework à la mode, en fait j'utilise simplement Guice + de simples servlets.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X