JavaRush /Blog Java /Random-FR /Héritage multiple en Java. Composition vs héritage
DSergey_Kh
Niveau 12

Héritage multiple en Java. Composition vs héritage

Publié dans le groupe Random-FR

Héritage multiple en Java

L'héritage multiple vous permet de créer une classe qui hérite de plusieurs superclasses. Contrairement à d'autres langages de programmation orientés objet populaires, tels que C++, Java n'autorise pas l'héritage multiple à partir des classes. Java ne prend pas en charge l'héritage de classes multiples car cela peut conduire au problème du diamant. Et au lieu de chercher des moyens de résoudre ce problème, il existe de meilleures options pour obtenir le même résultat, comme l’héritage multiple.

Problème de diamant

Pour comprendre plus facilement le problème du diamant, supposons que l'héritage multiple soit pris en charge en Java. Dans ce cas, nous pourrions avoir une hiérarchie de classes comme le montre l'image ci-dessous. Héritage multiple en Java.  Composition vs héritage - 1Supposons que la classe SuperClasssoit abstraite et qu'une méthode y soit déclarée. Les cours concrets ClassAet ClassB.
package com.journaldev.inheritance;
public abstract class SuperClass {
	public abstract void doSomething();
}
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of A");
	}
	//ClassA own method
	public void methodA(){
	}
}
package com.journaldev.inheritance;
public class ClassB extends SuperClass{
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of B");
	}
	//ClassB specific method
	public void methodB(){
	}
}
Supposons maintenant que nous voulions l’implémenter ClassCet en hériter de ClassAand ClassB.
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
	public void test(){
		//calling super class method
		doSomething();
	}
}
Notez que la méthode test()appelle la méthode superclasse doSomething(). Cela conduit à une ambiguïté car le compilateur ne sait pas quelle méthode de superclasse exécuter. Il s'agit d'un diagramme de classes en forme de losange appelé problème de losange. C'est la principale raison pour laquelle Java ne prend pas en charge l'héritage multiple. Notez que le problème ci-dessus avec l'héritage de plusieurs classes ne peut se produire qu'avec trois classes ayant au moins une méthode commune.

Héritage d'interfaces multiples

En Java, l'héritage multiple n'est pas pris en charge dans les classes, mais il l'est dans les interfaces. Et une interface peut étendre de nombreuses autres interfaces. Vous trouverez ci-dessous un exemple simple.
package com.journaldev.inheritance;
public interface InterfaceA {
	public void doSomething();
}
package com.journaldev.inheritance;
public interface InterfaceB {
	public void doSomething();
}
Notez que les deux interfaces déclarent la même méthode. Nous pouvons maintenant créer une interface qui étend ces deux interfaces, comme le montre l'exemple ci-dessous.
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
	//same method is declared in InterfaceA and InterfaceB both
	public void doSomething();
}
Cela fonctionne très bien car les interfaces déclarent uniquement des méthodes et l'implémentation sera effectuée dans les classes qui héritent de l'interface. Ainsi, il n’y a aucun moyen d’obtenir une ambiguïté dans l’héritage de plusieurs interfaces.
package com.journaldev.inheritance;
public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {
	@Override
	public void doSomething() {
		System.out.println("doSomething implementation of concrete class");
	}
	public static void main(String[] args) {
		InterfaceA objA = new InterfacesImpl();
		InterfaceB objB = new InterfacesImpl();
		InterfaceC objC = new InterfacesImpl();

		//all the method calls below are going to same concrete implementation
		objA.doSomething();
		objB.doSomething();
		objC.doSomething();
	}
}
Veuillez noter que chaque fois que vous remplacez une méthode de superclasse ou implémentez une méthode d'interface, utilisez l'annotation @Override. Et si on veut utiliser une fonction methodA()de classe ClassAet une fonction methodB()de classe ClassBdans une classe ClassC? La solution réside dans l’utilisation de la composition. Vous trouverez ci-dessous une version de la classe ClassCqui utilise la composition pour définir à la fois les méthodes de classe et la méthode doSomething()de l'un des objets.
package com.journaldev.inheritance;
public class ClassC{
	ClassA objA = new ClassA();
	ClassB objB = new ClassB();
	public void test(){
		objA.doSomething();
	}
	public void methodA(){
		objA.methodA();
	}
	public void methodB(){
		objB.methodB();
	}
}

Composition vs héritage

L'une des meilleures pratiques de programmation Java consiste à « approuver la composition avant l'héritage ». Nous explorerons certains des aspects qui favorisent cette approche.
  1. Disons que nous avons une superclasse et une classe qui l'étend :

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    }
    package com.journaldev.inheritance;
    public class ClassD extends ClassC{
    	public int test(){
    		return 0;
    	}
    }

    Le code ci-dessus se compile et fonctionne bien. Mais que se passe-t-il si nous modifions l’implémentation de la classe ClassCcomme indiqué ci-dessous :

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    	public void test(){
    	}
    }

    Notez que la méthode test()existe déjà dans la sous-classe, mais le type de retour est différent. Désormais, la classe ClassDne sera plus compilée et si vous utilisez un IDE, elle vous demandera de modifier le type de retour dans la superclasse ou la sous-classe.

    Imaginez maintenant une situation dans laquelle nous avons une hiérarchie d'héritage de classes à plusieurs niveaux et n'avons pas accès à la superclasse. Nous n'aurons d'autre choix que de changer la signature de notre méthode de sous-classe ou son nom pour supprimer l'erreur de compilation. Nous devrons également changer la méthode de la sous-classe à tous les endroits où elle est appelée. Ainsi, l’héritage rend notre code fragile.

    Le problème ci-dessus ne se produira jamais avec la composition, ce qui la rend plus attrayante pour l'héritage.

  2. Un autre problème avec l'héritage est que nous exposons toutes les méthodes de la superclasse au client et si notre superclasse n'est pas correctement conçue et qu'il y a des failles de sécurité, alors même si nous implémentons la meilleure implémentation de notre classe, nous sommes affectés par la mauvaise implémentation. de la superclasse. La composition nous aide à fournir un accès contrôlé aux méthodes de superclasse, alors que l'héritage ne permet pas de contrôler les méthodes de superclasse. C’est aussi l’un des principaux avantages de la composition issue de l’héritage.

  3. Un autre avantage de la composition est qu’elle permet une flexibilité dans l’appel des méthodes. Notre implémentation de la classe ClassCprésentée ci-dessus n'est pas optimale et garantit que le temps de compilation est lié à la méthode qui sera appelée. Avec des changements minimes, nous pouvons rendre l'appel de méthode flexible et dynamique.

    package com.journaldev.inheritance;
    public class ClassC{
    	SuperClass obj = null;
    	public ClassC(SuperClass o){
    		this.obj = o;
    	}
    	public void test(){
    		obj.doSomething();
    	}
    	public static void main(String args[]){
    		ClassC obj1 = new ClassC(new ClassA());
    		ClassC obj2 = new ClassC(new ClassB());
    
    		obj1.test();
    		obj2.test();
    	}
    }

    Le résultat du programme présenté ci-dessus :

    doSomething implementation of A
    doSomething implementation of B

    Cette flexibilité dans l'appel de méthode n'est pas disponible avec l'héritage, ce qui ajoute un autre avantage au choix de composition.

  4. Les tests unitaires sont plus faciles à réaliser avec la composition car nous savons que nous utilisons toutes les méthodes de la superclasse et pouvons les copier pour le test. Alors qu’en héritage nous dépendons davantage de la superclasse et ne connaissons pas toutes les méthodes de la superclasse qui seront utilisées. Nous devons donc tester toutes les méthodes de la superclasse, ce qui représente un travail supplémentaire dû à l'héritage.

    Idéalement, nous ne devrions utiliser l'héritage que lorsque la relation sous-classe-superclasse est définie comme "est". Dans tous les autres cas, il est recommandé d'utiliser la composition.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION