Je me demandais pourquoi, dans Java, les constructeurs ne sont pas hérités? Vous savez quand vous avez un cours comme celui-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 du fait qu'aucun constructeur par défaut n'a été 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, pas DRY et inutile (à mon humble avis) ... ce qui pose à nouveau la question:
Pourquoi Java ne supporte pas l'héritage de constructeur? Y a-t-il un avantage à ne pas autoriser cet héritage?
Supposons que les constructeurs étaient hérités ... puis, parce que chaque classe dérive finalement de Object, every class aboutira avec un constructeur sans paramètre. C'est une mauvaise idée. Qu'attendriez-vous exactement?
FileInputStream stream = new FileInputStream();
faire?
Maintenant, potentiellement, il devrait 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 à la construction d'une sous-classe sont souvent différents de ceux requis par la super-classe.
Lorsque vous héritez de Super, voici 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();
}
}
Donc, c'est la raison, car vous devez appeler votre constructeur unique, car "Super" n'en a pas par défaut.
Maintenant, essayez de deviner pourquoi Java ne prend pas en charge l'héritage de constructeur, probablement parce qu'un constructeur n'a de sens que s'il parle d'instances concrètes et que vous ne devriez pas pouvoir créer une instance de quelque chose sans connaître la définition. (par polymorphisme).
Parce que la construction de votre objet de sous-classe peut se faire de manière différente de celle de votre super-classe. Vous ne voulez peut-être pas que les clients de la sous-classe puissent appeler certains constructeurs disponibles dans la superclasse.
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
Dans cet exemple, vous voyez qu'il vous faut un moyen de déclarer "Je veux hériter de ces constructeurs" ou "Je veux hériter de tous les constructeurs à l'exception de ceux-ci", puis vous devez également spécifier un héritage de constructeur par défaut. préférence au cas où quelqu'un ajouterait un nouveau constructeur dans la super-classe ... ou vous pourriez simplement exiger que vous répétiez les constructeurs de la super-classe si vous voulez les "hériter", ce qui est sans doute le moyen le plus évident de le faire.
Parce que les constructeurs sont un détail d'implémentation - ce n'est pas quelque chose qu'un utilisateur d'une interface/super-classe peut invoquer. Au moment où ils obtiennent une instance, il a déjà été construit; et inversement, au moment de la construction d'un objet, il n'y a par définition aucune variable à laquelle il est actuellement affecté.
Réfléchissez à ce que cela signifierait de forcer toutes les sous-classes à avoir un constructeur hérité. Je soutiens qu'il est plus clair de passer les variables directement que pour que la classe ait "par magie" un constructeur avec un certain nombre d'arguments simplement parce que c'est le parent qui le fait.
La réponse de David est correcte. Je voudrais ajouter que vous obtiendrez peut-être un signe de Dieu indiquant que votre conception est gâchée, et que "Son" ne doit pas être une sous-classe de "Super", mais que Super a au contraire quelques détails de mise en œuvre mieux exprimés par avoir la fonctionnalité fournie par Son, en tant que stratégie.
EDIT: La réponse de Jon Skeet est incroyable.
Les constructeurs ne sont pas polymorphes.
Lorsque vous traitez avec des classes déjà construites, vous pouvez avoir affaire au type déclaré de l'objet, ou à l'une de ses sous-classes. C'est pour cela que l'héritage est utile.
Les constructeurs sont toujours appelés sur le type spécifique, par exemple new String()
. Les sous-classes hypothétiques n’ont aucun rôle à jouer à cet égard.
Parce qu'une (super) classe doit avoir un contrôle complet sur la manière dont elle est construite. Si le programmeur décide qu'il n'est pas judicieux de fournir un constructeur par défaut (sans argument) dans le cadre du contrat de la classe, le compilateur ne devrait pas en fournir un.
En fait, vous héritez des éléments constitutifs en ce sens que vous pouvez simplement appeler super si et lorsque cela est approprié. Cela signifie simplement que les raisons que d'autres ont évoquées seraient sujettes aux erreurs. Le compilateur ne peut pas présumer quand cela est approprié ou non.
La tâche du compilateur est de fournir autant de flexibilité que possible tout en réduisant la complexité et le risque d’effets secondaires non désirés.