web-dev-qa-db-fra.com

Héritage constructeur Java

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?

169
Pablo Fernandez

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.

188
Jon Skeet

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

27
David Santamaria

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. 

11
gustafc

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.

5
Andrzej Doyle

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.

2
Jonathan Feinberg

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.

2
Rik

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.

0
David R Tribble

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.

0
clearlight