Le polymorphisme est un concept fondamental dans la programmation orientée objet (POO) et joue un rôle crucial dans la conception et le développement de logiciels modulaires et extensibles. En Java, le polymorphisme permet à une entité, comme une méthode ou un objet, de prendre plusieurs formes. Cela facilite la réutilisabilité du code et permet une plus grande flexibilité et maintenabilité des applications. Cet article se propose de détailler le polymorphisme en Java, ses types, son importance, et ses applications pratiques.
Le terme « polymorphisme » provient du grec ancien et signifie « plusieurs formes ». En programmation, il désigne la capacité d’une méthode ou d’un objet à se comporter de différentes manières selon le contexte. Il existe principalement deux types de polymorphisme en Java :
Le polymorphisme à la compilation, également connu sous le nom de surcharge de méthodes (method overloading), permet à plusieurs méthodes d’avoir le même nom mais des signatures différentes (paramètres différents). Le compilateur détermine quelle méthode appeler en fonction des arguments passés.
Exemple :
class MathOperations {
// Surcharge de la méthode 'add'
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
String add(String a, String b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
MathOperations math = new MathOperations();
System.out.println(math.add(2, 3)); // Appelle add(int, int)
System.out.println(math.add(2.5, 3.7)); // Appelle add(double, double)
System.out.println(math.add("Hello, ", "World!")); // Appelle add(String, String)
}
} Le polymorphisme à l’exécution, ou substitution de méthodes (method overriding), permet à une sous-classe de fournir une implémentation spécifique d’une méthode déjà définie dans sa superclasse. Cette technique est couramment utilisée en Java pour permettre aux classes dérivées de spécifier leur propre comportement tout en maintenant une interface commune.
Exemple :
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.sound(); // Appelle Dog's sound()
myAnimal = new Cat();
myAnimal.sound(); // Appelle Cat's sound()
}
} Le polymorphisme offre plusieurs avantages importants :
Le polymorphisme est largement utilisé dans les frameworks et les bibliothèques Java. Par exemple, dans les interfaces graphiques utilisateur (GUI), des composants comme les boutons, les champs de texte, et les étiquettes peuvent tous être traités de manière polymorphique grâce à une interface commune comme Component. Cela permet de créer des interfaces utilisateur flexibles et modulaires.
Un autre exemple est l’utilisation des collections en Java. Les interfaces comme List, Set, et Map permettent de manipuler des collections d’objets de manière polymorphique, indépendamment de leur implémentation concrète.
Exemple avec les collections :
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
for (Animal animal : animals) {
animal.sound(); // Appelle la méthode sound() appropriée pour chaque objet
}
}
} Le polymorphisme est une caractéristique essentielle de la programmation orientée objet en Java. Il permet aux développeurs de concevoir des systèmes flexibles, modulaires, et facilement extensibles. Comprendre et utiliser le polymorphisme efficacement peut grandement améliorer la qualité et la maintenabilité du code.
Le polymorphisme en Java est une technique puissante qui, lorsqu’elle est bien appliquée, peut transformer la manière dont les logiciels sont conçus et développés.
Les interfaces jouent un rôle clé dans le polymorphisme en Java. Une interface peut être implémentée par plusieurs classes, permettant à des objets de ces classes d’être traités de manière polymorphique.
Exemple :
interface Drawable {
void draw();
}
class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
class Rectangle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a Rectangle");
}
}
public class Main {
public static void main(String[] args) {
Drawable d1 = new Circle();
Drawable d2 = new Rectangle();
d1.draw(); // Appelle draw() de Circle
d2.draw(); // Appelle draw() de Rectangle
}
} Java ne supporte pas l’héritage multiple direct (une classe ne peut pas hériter de plusieurs classes), mais il supporte l’héritage multiple indirect via les interfaces. Une classe peut implémenter plusieurs interfaces, offrant ainsi différentes formes de polymorphisme.
Exemple :
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Duck is flying");
}
@Override
public void swim() {
System.out.println("Duck is swimming");
}
}
public class Main {
public static void main(String[] args) {
Duck duck = new Duck();
duck.fly();
duck.swim();
// Polymorphisme via interfaces
Flyable flyableDuck = new Duck();
flyableDuck.fly();
Swimmable swimmableDuck = new Duck();
swimmableDuck.swim();
}
} Les classes abstraites permettent également le polymorphisme. Une classe abstraite peut avoir des méthodes abstraites que les sous-classes doivent implémenter, ainsi que des méthodes concrètes.
Exemple :
abstract class Shape {
abstract void draw();
void printDetails() {
System.out.println("This is a shape.");
}
}
class Triangle extends Shape {
@Override
void draw() {
System.out.println("Drawing a Triangle");
}
}
class Square extends Shape {
@Override
void draw() {
System.out.println("Drawing a Square");
}
}
public class Main {
public static void main(String[] args) {
Shape s1 = new Triangle();
Shape s2 = new Square();
s1.draw(); // Appelle draw() de Triangle
s2.draw(); // Appelle draw() de Square
s1.printDetails(); // Appelle printDetails() de Shape
s2.printDetails(); // Appelle printDetails() de Shape
}
} Les méthodes génériques permettent d’écrire du code polymorphique indépendant du type spécifique des objets manipulés. Les collections Java utilisent souvent ce type de polymorphisme.
Exemple :
import java.util.ArrayList;
import java.util.List;
public class Main {
public static <T> void addToList(T element, List<T> list) {
list.add(element);
}
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
addToList("Hello", stringList);
addToList(123, intList);
System.out.println(stringList); // Affiche [Hello]
System.out.println(intList); // Affiche [123]
}
} Le pattern Factory utilise le polymorphisme pour créer des objets sans exposer la logique de création au client. Ce pattern permet de créer des objets en fonction de paramètres dynamiques.
Exemple :
abstract class Animal {
abstract void makeSound();
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}
class AnimalFactory {
public static Animal getAnimal(String type) {
if (type.equals("Dog")) {
return new Dog();
} else if (type.equals("Cat")) {
return new Cat();
}
return null;
}
}
public class Main {
public static void main(String[] args) {
Animal a1 = AnimalFactory.getAnimal("Dog");
Animal a2 = AnimalFactory.getAnimal("Cat");
a1.makeSound(); // Appelle makeSound() de Dog
a2.makeSound(); // Appelle makeSound() de Cat
}
} Ces cas particuliers illustrent la diversité et la puissance du polymorphisme en Java. Il est essentiel pour les développeurs de comprendre et d’appliquer le polymorphisme afin de tirer pleinement parti de la programmation orientée objet en Java.
Java 8 a introduit les Streams, qui permettent de travailler avec des collections de manière fonctionnelle. Le polymorphisme joue un rôle clé dans cette API, permettant de traiter différentes collections de manière uniforme.
Exemple :
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
abstract class Employee {
String name;
double salary;
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
abstract double getAnnualBonus();
}
class Manager extends Employee {
Manager(String name, double salary) {
super(name, salary);
}
@Override
double getAnnualBonus() {
return salary * 0.1;
}
}
class Developer extends Employee {
Developer(String name, double salary) {
super(name, salary);
}
@Override
double getAnnualBonus() {
return salary * 0.2;
}
}
public class Main {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Manager("Alice", 100000),
new Developer("Bob", 80000),
new Manager("Charlie", 120000),
new Developer("David", 95000)
);
double totalBonuses = employees.stream()
.mapToDouble(Employee::getAnnualBonus)
.sum();
System.out.println("Total Annual Bonuses: " + totalBonuses);
}
} Les design patterns comme le Strategy et le Visitor exploitent le polymorphisme pour offrir des solutions flexibles à des problèmes récurrents.
Exemple du Pattern Strategy :
interface PaymentStrategy {
void pay(int amount);
}
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card ending in " + cardNumber);
}
}
class PayPalPayment implements PaymentStrategy {
private String email;
PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal email " + email);
}
}
class ShoppingCart {
private PaymentStrategy paymentStrategy;
ShoppingCart(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}
public class Main {
public static void main(String[] args) {
ShoppingCart cart1 = new ShoppingCart(new CreditCardPayment("1234"));
cart1.checkout(100);
ShoppingCart cart2 = new ShoppingCart(new PayPalPayment("example@example.com"));
cart2.checkout(200);
}
} Les interfaces fonctionnelles et les expressions lambda de Java 8 exploitent également le polymorphisme.
Exemple :
import java.util.function.Function;
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Function<String, Integer> stringLength = String::length;
Predicate<Integer> isEven = x -> x % 2 == 0;
String testString = "Hello, World!";
int length = stringLength.apply(testString);
if (isEven.test(length)) {
System.out.println("The length of the string is even.");
} else {
System.out.println("The length of the string is odd.");
}
}
} Les annotations et la réflexion permettent de créer des frameworks où le comportement peut être modifié dynamiquement en fonction des annotations présentes sur les classes ou les méthodes.
Exemple :
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {
}
class TestCase {
@Test
public void testMethod1() {
System.out.println("testMethod1 executed");
}
@Test
public void testMethod2() {
System.out.println("testMethod2 executed");
}
public void nonTestMethod() {
System.out.println("nonTestMethod executed");
}
}
public class Main {
public static void main(String[] args) throws Exception {
TestCase testCase = new TestCase();
Method[] methods = TestCase.class.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Test.class)) {
method.invoke(testCase);
}
}
}
} Ces exemples avancés montrent comment le polymorphisme en Java peut être utilisé pour créer des systèmes complexes et flexibles. En exploitant le polymorphisme avec des collections, des design patterns, des interfaces fonctionnelles, et la réflexion, les développeurs peuvent concevoir des solutions élégantes et extensibles à des problèmes logiciels variés. La compréhension et la maîtrise de ces techniques permettent de tirer pleinement parti des capacités offertes par Java et la programmation orientée objet.
Télécharger un guide complet sur le polymorphisme en Java :
Deux outils concrets pour piloter la qualité sans alourdir vos équipes Un système qualité n’avance…
Un chantier se gagne souvent avant même l’arrivée des équipes. Quand tout est clair dès…
Le mariage a du sens quand il repose sur une décision libre, mûrie et partagée.…
Une étude de cas réussie commence par une structure sûre. Ce modèle Word vous guide…
Les soft skills se repèrent vite sur une fiche, mais elles ne pèsent vraiment que…
Outil de comparaison et repérage des offres étudiantes Choisir des verres progressifs ressemble rarement à…
This website uses cookies.