Aller au contenu

Interfaces

L’exemple qui va suivre montre comment les interfaces permettent de définir un contrat commun que différentes classes peuvent implémenter, tout en gardant leur propre comportement car en java, il n’y a pas d’héritage multiple.

Exemple : Les animaux et leurs capacités

1. Problématique

Imaginons que nous voulons modéliser différents animaux (chien, oiseau, poisson) et que certains d’entre eux savent nager, voler ou courir. Plutôt que de créer une hiérarchie de classes complexe (avec héritage multiple, impossible en Java), nous allons utiliser des interfaces pour définir ces capacités.

Définition des interfaces

Une interface est un contrat qui définit des méthodes que les classes doivent implémenter.

Voici 3 interfaces pour nos capacités :

//interface pour les animaux qui savent nager
public interface Nager {
    void nager();
}
// interface pour les animaux qui savent voler
public interface Voler {
    void voler();
}
// pour les animaux qui savent courir
public interface Courir {
    void courir();
}

Implémentation des classes d’animaux

Chaque classe d’animal implémente les interfaces qui correspondent à ses capacités.

Classe Chien (implémente Courir et Nager)

public class Chien implements Courir, Nager {
    private String nom;

    public Chien(String nom) {
        this.nom = nom;
    }

    @Override
    public void courir() {
        System.out.println(nom + " court à quatre pattes !");
    }

    @Override
    public void nager() {
        System.out.println(nom + " nage en battant des pattes ! Enfin, on espère");
    }
}

Classe Oiseau (implémente Voler et Courir)

public class Oiseau implements Voler, Courir {
    private String nom;

    public Oiseau(String nom) {
        this.nom = nom;
    }

    @Override
    public void voler() {
        System.out.println(nom + " vole dans le ciel !");
    }

    @Override
    public void courir() {
        System.out.println(nom + " saute en marchant !");
    }
}

Classe Poisson (implémente Nager)

public class Poisson implements Nager {
    private String nom;

    public Poisson(String nom) {
        this.nom = nom;
    }

    @Override
    public void nager() {
        System.out.println(nom + " nage en ondulant !");
    }
}


## Utilisation des interfaces

>Dans la classe principale, nous pouvons maintenant traiter tous les animaux de manière polymorphique, en fonction de leurs capacités.

```java

public class Main {
    public static void main(String[] args) {

        Chien monChien = new Chien("Rex");
        Oiseau monOiseau = new Oiseau("Piaf");
        Poisson monPoisson = new Poisson("Nemo");

        // animaux qui savent courir
        Courir[] ceuxQuiCourent = {monChien, monOiseau};
        for (Courir animal : ceuxQuiCourent) {
            animal.courir();
        }

        // animaux qui savent nager
        Nager[] CeuxQuinagent = {monChien, monPoisson};
        for (Nager animal : CeuxQuinagent) {
            animal.nager();
        }

        // animaux qui savent voler
        Voler[] ceuxQuiVolent = {monOiseau};
        for (Voler animal : ceuxQuiVolent) {
            animal.voler();
        }
    }
}

Résultat attendu

Rex court à quatre pattes !
Piaf saute en marchant !
Rex nage en battant des pattes !
Nemo nage en ondulant !
Piaf vole dans le ciel !

Pourquoi utiliser des interfaces ?

Question

Tout dans une ArrayList : Quelle solution ?

Si on crée une liste générique **ArrayList**, on ne peut pas directement appeler **nager()**, **voler()** ou **courir()** sur les éléments de la liste, car le compilateur ne sait pas si l'objet sait Nager, un Voler ou Courir.

Les solutions :

Utiliser instanceof pour vérifier le type

Vérifier le type de chaque animal avec instanceof et caster l’objet vers l’interface appropriée.

public class Main {
    public static void main(String[] args) {

        ArrayList<Animal> animaux = new ArrayList<>();

        animaux.add(new Chien("Rex"));
        animaux.add(new Oiseau("Piaf"));
        animaux.add(new Poisson("Nemo"));

        for (Animal animal : animaux) {
            if (animal instanceof Nager) {
                ((Nageur) animal).nager();  // cast vers Nager
            }
            if (animal instanceof Voler) {
                ((Voleur) animal).voler();  // Cast vers Voler
            }
            if (animal instanceof Courir) {
                ((Coureur) animal).courir();  // Cast vers Courir
            }
        }
    }
}

Avantages :

Inconvénients :

Utiliser une méthode générique dans la classe Animal

On peut ajouter une méthode dans la classe Animal qui délègue l’appel aux interfaces. Cela évite de faire des instanceof dans le code client. Personnellement, c’est celle que je préfère.

public abstract class Animal {
    public abstract void faireNager();  // vide par défaut
    public abstract void faireVoler();
    public abstract void faireCourir();
}

// Implémentation dans la classe Chien
public class Chien extends Animal implements Nager, Courir {
    @Override
    public void faireNager() {
        this.nager();  // appelle la méthode de l'interface Nager
    }

    @Override
    public void faireVoler() {
        // ne fait rien, un chien ne vole pas quoique un jour peut-être.. avec la génétique
    }

    @Override
    public void faireCourir() {
        this.courir();  //appelle la méthode de l'interface Courir
    }

    @Override
    public void nager() {
        System.out.println("Le chien nage !");
    }

    @Override
    public void courir() {
        System.out.println("Le chien court !");
    }
}

Utilisation dans Main

public class Main {
    public static void main(String[] args) {
        ArrayList<Animal> animaux = new ArrayList<>();
        animaux.add(new Chien("Rex"));
        animaux.add(new Oiseau("Piaf"));
        animaux.add(new Poisson("Nemo"));

        // et là, ça marche dans tous les cas !
        for (Animal animal : animaux) {
            animal.faireNager();
            animal.faireVoler();
            animal.faireCourir();
        }
    }
}

Avantages :

Inconvénients : On doit implémenter des méthodes vides pour les capacités non supportées (faireVoler() dans Chien).