IFT1025, Programmation 2 Jian-Yun Nie Interface et Classe IFT1025, Programmation 2 Jian-Yun Nie
Concepts importants Interface: un contrat qu’une classe doit se conformer Favorise la réutilisation des codes Il suffit de savoir qu’une classe se conforme à une interface pour pouvoir l’utiliser (sans connaître les détails de son implantation) Classe normale: une classe implantée (dont les méthodes sont toutes définies) Classe abstraite: une classe qui n’est pas complètement implantée Mi-chemin entre classe normale et interface
Utilisation des classe Soit la classe BankAccount et les sous-classes CheckingAccount et SavingsAccount On peut faire des opérations en commun sur l’ensemble d’objets de la classe BankAccount (y compris ceux des sous-classe), Principe: Pour tout objet ref de BankAccount: ref.operation Conditions: operation est définie pour la classe mère (pour que ref.operation ne gégère pas une erreur de compilation) Operation est héritée ou redéfinie dans les sous-classes (polymorphisme) Polymorphisme assure qu’on utilise la version de l’instance
Exemple trivial: trouver balance max public BankAccount maxAccount(BankAccount [] compte) { BankAccount ref, refMax; int i; double max = 0; for (i=0; i<compte.length; i++) ref = compte[i]; if (ref.getBalance() > max) max = ref.getBalance(); refMax=ref; } return refMax; C’est possible grâce à getBalance() dans la classe BankAccount (et héritée dans les sous-classes) Utilisation: BankAcount meilleurClient; BankAccount [] clients = {new BankAccount(100), new CheckingAccount(10), //Petit tableau de 3 éléments new SavingsAccount(0.05)} … // des manipulations sur ces comptes meilleirClient = maxAccount(clients); Avantage de l’héritage et l’homogénéité dans une classe
Cas non trivial On ne peut pas implanter une méthode pour la classe mère, mais seulement dans les sous-classes Exemple: Classe: Forme: draw() non implantables Sous-classes: Rectangle: draw() implanté Rond: draw() implantée Mais on veut quand même utiliser la référence ref de classe Forme pour ref.draw()
Généralisation On veut créer des opérations applicables pour toutes les instances d’une classe Si une méthode est implantée pour la classe mère et toutes les sous-classe (héritée ou redéfinie) Cas trivial: la méthode est applicable Deux cas non triviaux: La méthode ne peut pas être implantée pour la classe mère, mais elle le peut pour les sous-classes On désire appeler la même méthode pour différentes classes (non reliées)
Premier cas: classe abstraite On veut définir une classe correspondant un concept (abstraite) englobant toutes les formes (rectangle, triangle, etc.) On sait que chaque forme a une surface On souhaite définir une méthode getSurface pour chaque forme Problème: Il existe pas une façon générale pour calculer la surface pour une forme Le calcul est possible seulement pour les sous-classes (Rectangle, Triangle, Rond, etc.)
Hiérarchie Forme: getSurface() Rectangle Triangle Rond … Non implantable Forme: getSurface() Rectangle Triangle Rond … getSurface() getSurface() getSurface() On ne veut pas créer une instance de Forme Solution: classe abstraite pour la classe mère
Deuxième cas On veut pouvoir appliquer une opération sur des objets Ex: comparer les objets de la même classe pour connaître un ordre: <, >, etc. Cette comparaison doit s’appliquer sur des classes très différentes BankAccount, Rectangle, etc. avec des critères différents Impossible d’utiliser classes abstraite pour ce cas Les classe ne sont pas sous la même classe mère
Illustration Object BankAccount Mot Personne Forme Ordre Object BankAccount Mot Personne Forme CheckingAccount Rectangle Triangle Rond SavingsAccount Classes pour lesquelles on veut définir l’ordre Impossible de regrouper les classes sous une autre classe mère Héritage simple Solution: interface
Classe abstraite Déclaration Exemple abstract public class Nom { … } abstract class Forme { int x, y; // coordonnée du début de la forme abstract float getSurface(); }
Caractéristiques de classe abstraite Déclaration de classe avec abstract Dans le corps {…}: On peut définir une méthode abstraite abstract float getSurface(); Méthode abstraite n’a pas de corps (non implantée) Mais une classe abstraite peut ne pas avoir de méthode abstraite On peut déclarer des attributs et implanter des méthodes On ne peut pas créer d’instance d’une classe abstraite new Forme(): Erreur de compilation
Sous-classes d’une classe abstraite class SousClasse extends Classe Si une sous-classe n’est pas abstraite Toutes les méthodes abstraites héritées doivent être implantées (avec un corps) Sinon, la sous-classe doit aussi être abstraite Instance possible pour une sous-classe non abstraite
Exemple abstract class Forme { int x, y; // coordonnée repère de la forme abstract float getSurface(); abstract void draw(); } public class Rectangle extends Forme int longueur, largeur; public float getSurface() return (float) longueur * largeur; public void draw() { … // dessiner le rectangle Une sous-classe concrète doit implanter toutes les méthodes abstraites héritées
Exemple (cont.) public class Rond extends Forme { int rayon; public float getSurface() … } // Une forme composée de deux autres formes abstract class FormeComposee extends Forme Forme f1, f2; Si une sous-classe n’implante pas toutes les méthodes abstraite, elle continue à être abstract Hérités: int x, y; abstract float getSurface(); abstract void draw();
Interface Interface = contrat = exigence de certains types de comportements Exigences applicables sur des classes différentes: BankAccount, Mot, … Exemple: ordonner les objets BankAccount: selon la balance Mot: selon l’ordre de mot, … Si on utilise seulement classe (abstraite): On doit définir ces comportements séparément dans chaque classe Pas moyen de regrouper ces objets
Utilisation désirée (exemple) Pour un groupe d’objets avec un comportement commun: ordre(Objet) Faire le même traitement de tri Programme général qui fonctionne sur tous les objets possédant ordre(Objet) Pour tous ref1 et ref2 de ce type ref1.ordre(ref2) >0, ref1.ordre(ref2) =0, ref1.ordre(ref2) <0 Réordonner selon leur comparaison Ce programme ne doit pas seulement fonctionner pour une classe Solution: interface
interface Une interface permet de regrouper les objets qui peuvent manifester le même type de comportement (mêmes méthodes) Interface n’a pas la même contrainte de héritage simple pour les classe Une classe peut se conformer à plusieurs interfaces Ex: Mot peut se comparer par ordre(…) et peut être mesuré en longueur, etc. Interface 1 pour ordre, interface 2 pour longueur, …
Définition d’une interface public interface Measurable { double getMeasure(); } Lister toutes les méthodes (comportements) désirées sans implantation (juste la signature) Méthodes sont automatiquement abstract et public Pas d’attribut (sauf constante)
Utiliser une interface Une classe se conforme à une interface: class BankAccount implements Measurable { … public double getMeasure() { return value; } La classe doit implanter toutes les méthodes exigées par l’interface
Utiliser une interface Une autre classe peut se conformer à la même interface public class Coin implements Measurable { public double getMeasure() return value; } . . . Mécanisme similaire à un héritage multiple, mais différent
Utiliser une interface On peut utiliser une interface comme un type Ex: déterminer le max et la somme: public class DataSet { . . . public void add(Measurable x) { sum = sum + x.getMeasure(); if (count == 0 || maximum.getMeasure() < x.getMeasure()) maximum = x; count++; } public Measurable getMaximum() return maximum; private double sum; private Measurable maximum; private int count; x est du type Measurable: - possède getMeasure()
Utiliser une interface 01: /** 02: This program tests the DataSet class. 03: */ 04: public class DataSetTester 05: { 06: public static void main(String[] args) 07: { 08: DataSet bankData = new DataSet(); 09: 10: bankData.add(new BankAccount(0)); 11: bankData.add(new BankAccount(10000)); 12: bankData.add(new BankAccount(2000)); 13: 14: System.out.println("Average balance = " 15: + bankData.getAverage()); 16: Measurable max = bankData.getMaximum(); 17: System.out.println("Highest balance = " 18: + max.getMeasure()); 19: 20: DataSet coinData = new DataSet(); 21: 22: coinData.add(new Coin(0.25, "quarter")); 23: coinData.add(new Coin(0.1, "dime")); 24: coinData.add(new Coin(0.05, "nickel")); 25: 26: System.out.println("Average coin value = " 27: + coinData.getAverage()); 28: max = coinData.getMaximum(); 29: System.out.println("Highest coin value = " 30: + max.getMeasure()); 31: } 32: } Average balance = 4000.0 Highest balance = 10000.0 Average coin value = 0.13333333333333333 Highest coin value = 0.25
Relation entre classe et interface class BankAccount implements Measuarable Convertir le type d’une classe à une interface BankAccount account = new BankAccount(10000); Measurable x = account; // OK System.out.println(x.getMeasure()); Casting une référence du type interface en une classe Ajouter un objet à DataSet DataSet coinData = new DataSet(); coinData.add(new Coin(0.25, "quarter")); coinData.add(new Coin(0.1, "dime")); . . . Measurable max = coinData.getMaximum(); // Get the largest coin String name = max.getName(); // ERROR Coin maxCoin = (Coin) max; String name = maxCoin.getName(); // OK
Utilisation d’interface générale Interface comme un type abstrait de données Liste Arbre binaire … Chaque type abstrait possède des méthodes « standard » Interface: exiger ces méthodes
Classe abstraite vs. Interface Certaines méthodes peuvent être abstraites Peut contenir des attributs Peut implanter des méthodes Héritage simple Interface = contrat Aucune implantation de méthode Différentes classes peuvent signer le même contrat Classe = famille posséder le même nom de famille a la même habileté (peut être une méthode abstraite) Contrat commun pour différentes familles
Exemple: Interface Movable
Interface Weapon «interface» wields Explorer Weapon Sword Pen MissileLauncher
“Héritage” Multiple d’interfaces par une classe class Sword implements Weapon, Movable {…}
Extension d’interface Assume all weapons are movable: interface Weapon extends Movable {…} Interface
Modifier une interface? Si une classe définie n’est plus suffisante Ajouter des attributs et des méthodes Les autres classes utilisant cette classe continuent à fonctionner Si une interface n’est plus suffisante Ne pas ajouter des méthodes abstraites Sinon, les classe qui sont conformes à l’interface ne le seront plus (très coûteux à modifier toutes ces classes!) On définit plutôt une sous-interface interface SousInterface extends SuperInterface
Extension multiple d’interface Une interface peut extends plusieurs autres interfaces interface DataIO extends DataInput, DataOutput { }
instanceOf … Tester si une instance est d’une classe (sous-classe) Fonctionne aussi pour tester si une instance est du type d’une interface (se conforme à une interface) Exemple: interface DataIO extends DataInput, DataOutput { } DataIO refInterface; BankAccount compte; … if (refInterface instanceOf DataOutput) … if (compte instanceOf Movable) …
Utilisation d’interface et de classe Interface comme type de donnée Partagent les méthodes Classe comme type de données Partagent les méthodes et attributs Les deux peuvent se convertir refInterface instance refClasse instance Casting: (Interface).refClasse: utile? refClasse peut faire la même chose que (Interface).refClasse. (Classe).refInterface: utile! Accéder à d’autres attributs et méthodes de la Classe
Exemple d’interface en Java Collection
Collection public interface Collection<E> extends Iterable<E> { //Basic operations int size(); boolean isEmpty(); boolean contains(Object element); boolean add(E element); //optional boolean remove(Object element); //optional Iterator iterator(); //Bulk operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); //optional boolean removeAll(Collection<?> c); //optional boolean retainAll(Collection<?> c); //optional void clear(); //optional //Array operations Object[] toArray(); <T> T[] toArray(T[] a); } <E>: Genetic
Traverser une collection (aperçu) public interface Iterator<E> { boolean hasNext(); E next(); void remove(); //optional } Utilisation: Supposons: boolean cond(Object) static void filter(Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) if (!cond(i.next())) i.remove();
List public interface List<E> extends Collection<E> { //Positional access E get(int index); E set(int index, E element); //optional boolean add(E element); //optional void add(int index, E element); //optional E remove(int index); //optional abstract boolean addAll(int index, Collection<? extends E> c); //optional //Search int indexOf(Object o); int lastIndexOf(Object o); //Iteration ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); //Range-view List<E> subList(int from, int to); }
ArrayList java.util Class ArrayList<E> java.lang.Object java.util.AbstractCollection<E> java.util.AbstractList<E> java.util.ArrayList<E> All Implemented Interfaces: Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess Hiérarchie des classes
Comparaison avec AbstractList public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { // Méthodes implantées: void add(int index, E element) {…} void clear() {…} int hashCode() … }
Inner class: concept Définir une classe qui a une utilité locale comme inner classs: public class DataSetTester3 { public static void main(String[] args) class RectangleMeasurer implements Measurer . . . } Measurer m = new RectangleMeasurer(); DataSet data = new DataSet(m); Dans une inner class, on peut accéder aux méthodes et attributs de la classe qui l’englobe (DataSetTesters)