Java n'autorise pas l'héritage multiple, mais il permet d'implémenter plusieurs interfaces. Pourquoi?
Java n'autorise pas l'héritage multiple, mais il permet d'implémenter plusieurs interfaces. Pourquoi?
Réponses:
Parce que les interfaces spécifient uniquement ce que fait la classe, pas comment elle le fait.
Le problème avec l'héritage multiple est que deux classes peuvent définir différentes manières de faire la même chose, et la sous-classe ne peut pas choisir laquelle choisir.
Un de mes instructeurs de collège m'a expliqué cela de cette façon:
Supposons que j'ai une classe, qui est un grille-pain, et une autre classe, qui est NuclearBomb. Ils peuvent tous les deux avoir un paramètre «obscurité». Ils ont tous deux une méthode on (). (L'un a un off (), l'autre pas.) Si je veux créer une classe qui est une sous-classe de ces deux ... comme vous pouvez le voir, c'est un problème qui pourrait vraiment exploser dans mon visage ici .
Donc, l'un des principaux problèmes est que si vous avez deux classes parentes, elles peuvent avoir des implémentations différentes de la même fonctionnalité - ou éventuellement deux fonctionnalités différentes avec le même nom, comme dans l'exemple de mon instructeur. Ensuite, vous devez décider lequel va utiliser votre sous-classe. Il existe des moyens de gérer cela, certainement - C ++ le fait - mais les concepteurs de Java ont estimé que cela compliquerait les choses.
Avec une interface, cependant, vous décrivez quelque chose que la classe est capable de faire, plutôt que d'emprunter la méthode d'une autre classe pour faire quelque chose. Plusieurs interfaces sont beaucoup moins susceptibles de provoquer des conflits délicats qui doivent être résolus que plusieurs classes parentes.
Parce que l'héritage est surutilisé même si vous ne pouvez pas dire "hé, cette méthode semble utile, je vais aussi étendre cette classe".
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
La réponse à cette question réside dans le fonctionnement interne du compilateur java (chaînage de constructeur). Si nous voyons le fonctionnement interne du compilateur java:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
Après avoir compilé, cela ressemble à:
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
lorsque nous étendons la classe et en créons un objet, une chaîne de constructeur fonctionnera jusqu'à Object
classe.
Le code ci-dessus fonctionnera bien. mais si nous avons une autre classe appelée Car
qui s'étend Bank
et une classe hybride (héritage multiple) appelée SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
Dans ce cas (SBICar) ne parviendra pas à créer la chaîne de constructeur ( ambiguïté de compilation ).
Pour les interfaces, cela est autorisé car nous ne pouvons pas en créer un objet.
Pour le nouveau concept default
et la static
méthode, veuillez vous référer à default dans l'interface .
J'espère que cela résoudra votre requête. Merci.
L'implémentation de plusieurs interfaces est très utile et ne pose pas beaucoup de problèmes aux développeurs de langage ni aux programmeurs. C'est donc permis. L'héritage multiple, tout en étant également utile, peut causer de graves problèmes aux utilisateurs ( diamant redouté de la mort ). Et la plupart des choses que vous faites avec l'héritage multiple peuvent également être effectuées par composition ou en utilisant des classes internes. L'héritage multiple est donc interdit car apportant plus de problèmes que de gains.
ToyotaCar
et les HybridCar
deux dérivaient de Car
et remplaçaient Car.Drive
, et s'ils PriusCar
héritaient des deux mais ne les remplaceraient pasDrive
, le système n'aurait aucun moyen d'identifier ce que le virtuel Car.Drive
devrait faire. Les interfaces évitent ce problème en évitant la condition en italique ci-dessus.
void UseCar(Car &foo)
; on ne peut pas s'attendre à ce qu'il inclue une désambiguïsation entre ToyotaCar::Drive
et HybridCar::Drive
(car il ne devrait souvent ni savoir ni se soucier que ces autres types existent même ). Un langage pourrait, comme le fait C ++, exiger que le code ToyotaCar &myCar
souhaitant le transmettre à UseCar
doit d'abord être converti en soit HybridCar
ou ToyotaCar
, mais puisque ((Car) (HybridCar) myCar) .Drive` et ((Car)(ToyotaCar)myCar).Drive
ferait des choses différentes, cela impliquerait que les upcasts ne préservaient pas l’identité.
Vous pouvez trouver une réponse précise pour cette requête dans la page de documentation d'Oracle sur l' héritage multiple
Héritage multiple d'état: possibilité d'hériter des champs de plusieurs classes
L'une des raisons pour lesquelles le langage de programmation Java ne vous permet pas d'étendre plus d'une classe est d'éviter les problèmes d'héritage multiple d'état, qui est la possibilité d'hériter des champs de plusieurs classes.
Si l'héritage multiple est autorisé et lorsque vous créez un objet en instanciant cette classe, cet objet hérite des champs de toutes les superclasses de la classe. Cela causera deux problèmes.
Héritage multiple d'implémentation: possibilité d'hériter des définitions de méthode de plusieurs classes
Problèmes avec cette approche: nommer les conflits et l' ambiguïté . Si une sous-classe et une superclasse contiennent le même nom de méthode (et la même signature), le compilateur ne peut pas déterminer la version à appeler.
Mais java prend en charge ce type d'héritage multiple avec les méthodes par défaut , qui ont été introduites depuis la version Java 8. Le compilateur Java fournit des règles pour déterminer la méthode par défaut utilisée par une classe particulière.
Reportez-vous au post SE ci-dessous pour plus de détails sur la résolution du problème de diamant:
Quelles sont les différences entre les classes abstraites et les interfaces dans Java 8?
Héritage multiple de type: Capacité d'une classe à implémenter plus d'une interface.
Comme l'interface ne contient pas de champs modifiables, vous n'avez pas à vous soucier des problèmes qui résultent de l'héritage multiple d'état ici.
On dit que l'état des objets est référencé par rapport aux champs qu'il contient et qu'il deviendrait ambigu si trop de classes étaient héritées. Voici le lien
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Java prend en charge l'héritage multiple via des interfaces uniquement. Une classe peut implémenter n'importe quel nombre d'interfaces mais ne peut étendre qu'une seule classe.
L'héritage multiple n'est pas pris en charge car il conduit à un problème de diamant mortel. Cependant, il peut être résolu mais cela conduit à un système complexe, de sorte que l'héritage multiple a été abandonné par les fondateurs de Java.
Dans un livre blanc intitulé «Java: un aperçu» de James Gosling en février 1995 ( lien ) donne une idée des raisons pour lesquelles l'héritage multiple n'est pas pris en charge en Java.
Selon Gosling:
"JAVA omet de nombreuses fonctionnalités rarement utilisées, mal comprises et déroutantes de C ++ qui, d'après notre expérience, apportent plus de problèmes que d'avantages. Cela consiste principalement en une surcharge d'opérateurs (bien qu'il y ait une surcharge de méthode), un héritage multiple et des coercitions automatiques étendues."
Pour la même raison, C # n'autorise pas l'héritage multiple mais vous permet d'implémenter plusieurs interfaces.
La leçon tirée du C ++ avec héritage multiple était qu'il conduisait à plus de problèmes qu'il n'en valait la peine.
Une interface est un contrat de choses que votre classe doit implémenter. Vous n'obtenez aucune fonctionnalité de l'interface. L'héritage vous permet d'hériter des fonctionnalités d'une classe parente (et en héritage multiple, cela peut devenir extrêmement déroutant).
Autoriser plusieurs interfaces vous permet d'utiliser des modèles de conception (comme Adapter) pour résoudre les mêmes types de problèmes que vous pouvez résoudre à l'aide de l'héritage multiple, mais d'une manière beaucoup plus fiable et prévisible.
D1
et les D2
deux héritent de B
, et chacun remplace une fonction f
, et si obj
est une instance d'un type S
qui hérite des deux D1
et D2
mais ne remplace pas f
, alors la conversion d'une référence en S
en D1
devrait donner quelque chose dont f
utilise le D1
remplacement, et la conversion en B
ne devrait pas changez cela. De même, le cast d'une référence S
vers D2
devrait produire quelque chose dont f
utilise le D2
remplacement, et le cast vers B
ne devrait pas changer cela. Si une langue n'avait pas besoin d'autoriser l'ajout de membres virtuels ...
Puisque ce sujet n'est pas proche, je publierai cette réponse, j'espère que cela aidera quelqu'un à comprendre pourquoi java n'autorise pas l'héritage multiple.
Considérez la classe suivante:
public class Abc{
public void doSomething(){
}
}
Dans ce cas, la classe Abc ne prolonge rien non? Pas si vite, cette classe étend implicitement la classe Object, classe de base qui permet à tout de fonctionner en java. Tout est un objet.
Si vous essayez d'utiliser la classe ci - dessus , vous verrez que votre IDE vous permettent d'utiliser des méthodes telles que : equals(Object o)
, toString()
, etc, mais vous ne l' avez pas déclaré ces méthodes, ils sont venus de la classe de baseObject
Tu pourrais essayer:
public class Abc extends String{
public void doSomething(){
}
}
C'est bien, car votre classe ne s'étendra pas implicitement Object
mais s'étendra String
parce que vous l'avez dit. Considérez le changement suivant:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
Maintenant, votre classe retournera toujours "hello" si vous appelez toString ().
Imaginez maintenant la classe suivante:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
Encore une fois la classe Flyer
implicite s'étend Object qui a la méthode toString()
, n'importe quelle classe aura cette méthode puisqu'elles s'étendent toutes Object
indirectement, donc, si vous appelez à toString()
partir de Bird
, quel toString()
Java devrait utiliser? De Abc
ou Flyer
? Cela se produira avec n'importe quelle classe qui essaie d'étendre deux classes ou plus, pour éviter ce genre de "collision de méthodes", ils ont construit l'idée d' interface , en gros vous pourriez les considérer comme une classe abstraite qui n'étend pas Object indirectement . Comme ils sont abstraits, ils devront être implémentés par une classe, ce qui est un objet(vous ne pouvez pas instancier une interface seule, ils doivent être implémentés par une classe), donc tout continuera à fonctionner correctement.
Pour différer les classes des interfaces, le mot clé implements était réservé uniquement aux interfaces.
Vous pouvez implémenter n'importe quelle interface que vous aimez dans la même classe puisqu'elles n'étendent rien par défaut (mais vous pouvez créer une interface qui étend une autre interface, mais encore une fois, l'interface "père" n'étend pas Object "), donc une interface est juste une interface et ils ne souffriront pas de " colissions de signatures de méthodes ", s'ils le font, le compilateur vous lancera un avertissement et vous devrez juste changer la signature de la méthode pour la corriger (signature = nom de la méthode + paramètres + type de retour) .
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
Parce qu'une interface n'est qu'un contrat. Et une classe est en fait un conteneur de données.
Par exemple deux classes A, B ayant la même méthode m1 (). Et la classe C étend à la fois A, B.
class C extends A, B // for explaining purpose.
Maintenant, la classe C recherchera la définition de m1. Tout d'abord, il cherchera en classe s'il n'a pas trouvé, puis il vérifiera la classe des parents. Les deux A, B ayant la définition Donc ici une ambiguïté se produit quelle définition doit choisir. Donc JAVA NE PREND PAS EN CHARGE DES HÉRITÉS MULTIPLES.
Java ne prend pas en charge l'héritage multiple pour deux raisons:
Object
classe. Lorsqu'elle hérite de plus d'une super classe, la sous-classe obtient l'ambiguïté pour acquérir la propriété de la classe Object.super()
pour invoquer le constructeur de la classe supper. Si la classe a plus d'une super classe, elle devient confuse.Ainsi, lorsqu'une classe s'étend à partir de plus d'une super classe, nous obtenons une erreur de compilation.
Prenons par exemple le cas où la classe A a une méthode getSomething et la classe B une méthode getSomething et la classe C étend A et B. Que se passerait-il si quelqu'un appelait C.getSomething? Il n'existe aucun moyen de déterminer la méthode à appeler.
Les interfaces spécifient simplement quelles méthodes une classe d'implémentation doit contenir. Une classe qui implémente plusieurs interfaces signifie simplement que la classe doit implémenter les méthodes de toutes ces interfaces. Whci n'entraînerait aucun problème comme décrit ci-dessus.
Considérez un scénario où Test1, Test2 et Test3 sont trois classes. La classe Test3 hérite des classes Test2 et Test1. Si les classes Test1 et Test2 ont la même méthode et que vous l'appelez à partir d'un objet de classe enfant, il y aura ambiguïté pour appeler la méthode de la classe Test1 ou Test2, mais il n'y a pas d'ambiguïté pour l'interface car dans l'interface aucune implémentation n'est là.
Java ne prend pas en charge l'héritage multiple, l'héritage multi-chemins et l'héritage hybride en raison d'un problème d'ambiguïté:
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
Lien de référence: https://plus.google.com/u/0/communities/102217496457095083679
de manière simple, nous le savons tous, nous pouvons hériter (étendre) une classe, mais nous pouvons implémenter tellement d'interfaces ... c'est parce que dans les interfaces, nous ne donnons pas d'implémentation, disons simplement la fonctionnalité. supposons que si java peut étendre autant de classes et que celles-ci ont les mêmes méthodes .. à ce stade, si nous essayons d'appeler une méthode de super classe dans la sous-classe, quelle méthode suppose d'exécuter ??, le compilateur devient confus exemple: - essayez d'étendre plusieurs mais dans interfaces ces méthodes n'ont pas de corps, nous devrions les implémenter dans la sous-classe .. essayez plusieurs implémentations donc pas de soucis ..
* C'est une réponse simple puisque je suis débutant en Java *
Considérez qu'il existe trois classes X
, Y
et Z
.
Nous héritons donc de X extends Y, Z
And both Y
et avons Z
une méthode alphabet()
avec le même type de retour et les mêmes arguments. Cette méthode alphabet()
en Y
dit afficher premier alphabet et alphabet méthode Z
dit affichage dernier alphabet . Alors, voici l'ambiguïté quand alphabet()
est appelé par X
. Que ce soit pour afficher le premier ou le dernier alphabet ??? Ainsi, java ne prend pas en charge l'héritage multiple. Dans le cas des interfaces, considérez Y
et Z
comme des interfaces. Donc les deux contiendront la déclaration de méthode alphabet()
mais pas la définition. Il ne dira pas s'il faut afficher le premier alphabet ou le dernier alphabet ou quoi que ce soit, mais déclarera simplement une méthodealphabet()
. Il n'y a donc aucune raison de soulever l'ambiguïté. Nous pouvons définir la méthode avec tout ce que nous voulons dans la classe X
.
Donc en un mot, dans Interfaces la définition se fait après implémentation donc pas de confusion.