Arnaud Labourel
24 ou 26 septembre 2018
Un objet :
Dans les langages orientés objet, une classe (d’objet) définit :
Quels sont les objets nécessaires à la résolution du problèmes ?
\(\Rightarrow\) décomposition du problème en objets
À quels modèles des objets correspondent-il ?
\(\Rightarrow\) Quelles sont les classes ?
Quels sont les fonctionnalités/opérations dont on doit/veut pouvoir disposer sur ces objets ?
\(\Rightarrow\) Quelles sont les méthodes des classes ?
Quelle est la structure des données de l’objet ?
\(\Rightarrow\) Quelles sont les attributs des classes ?
un catalogue regroupe des articles, il permet de trouver un article à partir de sa référence.
un article est caractérisé par un prix et une référence que l’on peut obtenir. On veux aussi pouvoir déterminer si un article est plus cher qu’un autre
une commande est créée pour un client et un catalogue donnés, on peut ajouter des articles à une commande, accéder à la liste des articles commandés ainsi que prix total des articles et le montant des frais de port de la commande.
un client peut créer une commande pour un catalogue et commander dans cette commande des articles à partir de leur références.
un catalogue regroupe des articles, il permet de trouver un article à partir de sa référence.
un article est caractérisé par un prix et une référence que l’on peut obtenir. On veux aussi pouvoir déterminer si un article est plus cher qu’un autre
une commande est créée pour un client et un catalogue donnés, on peut ajouter des articles à une commande, accéder à la liste des articles commandés ainsi que prix total des articles et le montant des frais de port de la commande.
un client peut créer une commande pour un catalogue et commander dans cette commande des articles à partir de leur références.
un catalogue regroupe des articles, il permet de trouver un article à partir de sa référence.
un article est caractérisé par un prix et une référence que l’on peut obtenir. On veux aussi pouvoir déterminer si un article est plus cher qu’un autre
une commande est créée pour un client et un catalogue donnés, on peut ajouter des articles à une commande, accéder à la liste des articles commandés ainsi que prix total des articles et le montant des frais de port de la commande.
un client peut créer une commande pour un catalogue et commander dans cette commande des articles à partir de leur références.
Catalog
un catalogue regroupe des articles, il permet de trouver un article à partir de sa référence.
Item getItem(String reference)
Item
un article est caractérisé par un prix et une référence que l’on peut obtenir. On veux aussi pouvoir déterminer si un article est plus cher qu’un autre
double getPrice()
String getReference()
boolean isMoreExpensiveThan(Item other)
Order
une commande est créée pour un client et un catalogue donnés, on peut ajouter des articles à une commande, accéder à la liste des articles commandés ainsi que prix total des articles et le montant des frais de port de la commande.
Order(Client client, Catalog catalog)
(constructeur)void addItem(Item item)
List<Item> allItems()
double getTotalPriceOfItems()
double getShippingCost()
Client getClient()
Catalog getCatalog()
Client
un client peut créer une commande pour un catalogue et commander dans cette commande des articles à partir de leur références.
Order createOrder(Catalog catalog)
void orderItem(Order order, String reference)
On souhaite créer une commande pour un client, faire commander deux articles par le client et obtenir le prix des articles.
On suppose les références suivantes disponibles et initialisées :
Client client = new Client( ... )
Catalog catalog = new Catalog( ... )
getTotalCost
On souhaite ajouter une méthode qui renvoie le coût total d’une commande.
On peut placer cette commande dans la classe Client
:
public double getTotalCost(Order order){
return order.getTotalPriceOfItems()
+ order.getShippingCost();
}
ou bien dans la classe order
:
createOrder
dans Client
this
this
= référence vers l’objet invoquant la méthodethis
toujours défini dans le contexte d’une méthode non-statiqueLa classe item
a un attribut price
.
Une méthode orderItem
dans la classe Client
:
Si l’attribut price
est public, le code suivant est valide :
public void orderItem(Order order, String reference){
Catalog catalog = order.getCatalog();
Item item = catalog.getItem(reference);
item.price = 0;
order.addItem(item);
}
On doit donc au possible restreindre l’accès aux attributs sensibles depuis l’extérieur.
Java
: modificateurs d’accès précisés lors de la définition d’attributs, de méthodes ou de constructeurs.private
: accessible uniquement pour les instances de la classes et donc uniquement depuis le code des méthodes ou des constructeurs de la classe.protected
: ??? (à voir dans un prochain cours)default
(lorsqu’on n’écrit aucun modificateur) : accessible uniquement par les classes du même packagepublic
: accessible depuis n’importe où.En java, un projet peut être découpé en paquets.
Les paquets permettent de :
Un paquet est une collection de classes.
Un classe indique son paquet au début du code :
Rendre privés les attributs caractérisant l’état de l’objet et fournir des méthodes publiques permettant de modifier/accéder à l’attribut.
La règle n’est pas absolue : les attributs immutables (mot-clé final
) d’une classe peuvent être public
.
Exemple : l’attribut length
des tableaux en Java.
public class Point{
private double radius;
private double angle;
public Point(double x, double y){
radius = Math.hypot(x,y);
angle = Math.atan2(y,x);
}
public getX(){ return Math.cos(angle) * radius; }
public getY(){ return Math.sin(angle) * radius; }
public void rotate(double angle){
this.angle += angle;
}
}
Papiers, bouteilles, piles électriques, cageots, … sont des objets différents :
\(\Rightarrow\) déchirer du papier, remplir un bouteille
Mais ces objets partagent tous la propriété d’être recyclable
\(\Rightarrow\) tous peuvent être recyclés (même si le processus peut varier)
On peut recycler tous les objets d’une poubelle.
Paper
, Bottle
, Battery
, Crate
, … sont des classes d’objets différentesrecycle()
avec une implémentation adaptée à chacune.Conserver les classes différentes et créer un type commun.
Paper
, Bottle
, …recycle
.On va projeter les objets sur un type commun qui ne gardera que la partie commune des fonctionnalités. On ne considère qu’une facette de l’objet.
interface
interface
est un ensemble de déclaration de signatures de méthodes et définit un type.Supposons que des classes implémentent un service de façons différentes :
Les instances de ces deux classes possèdent une méthode print
avec la même signature (types des arguments et du retour).
Nous souhaiterions pouvoir facilement passer du code suivant :
au code suivant :
Il nous faudrait définir un type Printer
qui oblige la variable à contenir des références vers des objets qui implémentent la méthode print
.
\(\Rightarrow\) définition d’une interface Printer
.
On peut vouloir traiter les objets en utilisant les services qu’ils partagent :
On peut aussi vouloir écrire un programme en supposant que les objets manipulés implémentent certains services (comme le fait de pouvoir les comparer) :
Description d’une interface en Java :
public interface Printer{
/**
* Affiche la chaîne de caractères document.
* @param document la chaîne à afficher
*/
public void print(String document);
}
Une interface :
Le mot-clé implements
permet d’indiquer qu’une classe implémente un interface :
class SimplePrinter implements Printer {
void print(String document){
System.out.println(document);
}
}
class BracePrinter implements Printer {
void print(String document){
System.out.println("{" + document + "}");
}
}
Java vérifie à la compilation que toutes les méthodes de l’interface sont implémentées.
Déclaration d’une variable de type référence vers une instance d’une classe qui implémente l’interface Printer
:
Une interface ne définit pas de constructeurs.
Interdit : printer = new Printer()
Pour affecter une référence à une variable d’un type défini par une interface, on doit instancier une classe implémentant l’interface.
Il est donc possible d’instancier une classe implémentant l’interface,
puis de stocker la référence de l’objet dans une variable de type de l’interface :
On parle alors d’upcasting (transtypage vers le haut). On peut aussi directement mettre un tel objet sans passer par une variable intermédiaire :
Printer printer2 = new BracePrinter();
Par contre, cela ne fonctionne pas dans le cas où la classe n’implémente pas l’interface :
Printer printer3 = new String("Hello!"); //interdit
L’existence des méthodes est vérifiée à la compilation.
Le code suivant ne compilera pas car l’interface Printer
n’a pas de méthode println
:
Du grec ancien polús (plusieurs) et morphê (forme), concept consistant à fournir une interface unique à des entités pouvant avoir différents types.
Le choix de la méthode à exécuter ne peut être fait qu’à l’exécution :
Printer[] printers = new Printer[2];
printers[0] = new SimplePrinter();
printers[1] = new BracePrinter();
Random random = new Random(); // générateur aléatoire
int index = random.nextInt(2); // 0 et 1
printers[index].print("mon message");
L’affichage dépend du tirage aléatoire pour index
:
SimplePrinter
\(\rightarrow\) mon messageBracePrinter
\(\rightarrow\) {mon message}Une interface est un ensemble de signatures de méthodes.
Une classe peut implémenter une interface : elle doit préciser le comportement de chacune des méthodes de l’interface.
Il est possible de déclarer une variable pouvant contenir des références vers des instances de classes qui implémentent l’interface.
Java vérifie à la compilation que toutes les affectations et les appels de méthodes sont corrects.
Le choix du code qui va être exécuté est décidé à l’exécution (en fonction de l’instance pointée par la référence).
Comparable<T>
: objets qu’on peut comparer à des objets de type T
.List<T>
: liste d’objets de type T
.Stack<E>
: pile d’objet de type T
.Iterable<T>
: collection d’objet de type T
qu’on peut parcourir avec un boucle.Types dont la définition contient un autre type.
Comparable<T>
public interface Comparable<T>{
/**
* Compares this object with the specified object for
* order. Returns a negative integer, zero, or a
* positive integer as this object is less than,
* equal to, or greater than the specified object.
* @param other the objet to be compared
* @return a negative integer, zero, or a positive
* integer as this object is less than, equal to, or
* greater than the specified object.
*/
int compareTo(T other);
}
Comparable<T>
// Utilisation typique
public class MyOrderedClass
implements Comparable<MyOrderedClass>{
int compareTo(MyOrderedClass other){
// ...
}
}
Sert à définir une relation d’ordre entre les objets d’une classe (par exemple pour les trier).
Comparable<T>
pour la classe Student
Comparable<T>
pour la classe Student
Iterable
public interface Iterable<T>{
Iterator<T> iterator();
void forEach(Consumer <? super T> action);
Spliterator<T> spliterator();
}
Iterable
Utilisation : si une classe implémente Iterable<T>
, ces instances contiennent une collection d’objets de type T
.
On peut parcourir les objets de la collection à l’aide d’une boucle for
.
Collection
public interface Collection<T> extends Iterable<T>{
boolean add(T element);
boolean contains(T element);
boolean isEmpty();
boolean remove(Object o);
//...
}
extends
Quand une interface “fille” étend une interface “mère”, elle hérite de toutes les méthodes de sa “mère”.
Une classe implémentant la classe “fille” doit donc définir les méthodes des deux interfaces.
Collection
Il existent de nombreuses interfaces qui sont une extension de l’interface Collection
:
Set
: collection d’objet dans laquelle chaque objet ne peut apparaitre qu’une fois. Implémenté par HashSet
et TreeSet
.List
: séquence d’éléments. Implémenté par ArrayList
et LinkedList
.Deque
(double ended queue) : séquence d’éléments avec accès qu’au début et à la fin (file d’attente). Implémenté par ArrayDeque
et LinkedList
.Il peut être utile d’avoir une classe implémentant plusieurs interfaces.
Par exemple, une classe Modem
pourrait implémenter les deux interfaces suivantes :
Une classe Printable
avec une méthode print
qui permet d’afficher l’objet.
Une classe Stack
avec deux méthodes :
push
qui permet d’empiler un entier.pop
dépile et retourne l’entier en haut de la pile.Implémentation des deux interfaces précédentes :
public class PrintableArrayStack
implements Stack, Printable {
private int[] array; private int size;
public PrintableArrayStack(int capacity) {
array = new int[capacity]; size = 0;
}
public void push(int v) { array[size] = v; size++; }
public int pop() { size--; return array[size]; }
public void print() {
for (int i = 0; i < size; i++)
System.out.print(array[i]+" ");
System.out.println();
}
}
Implémentation d’une des deux interfaces :
Exemple d’utilisation des classes précédentes :
Printable[] printables = new Printable[3];
printables[0] = new PrintableString("bonjour");
PrintableArrayStack stack = new PrintableArrayStack(10);
printables[1] = stack;
printables[2] = new PrintableString("salut");
stack.push(10);
stack.push(30); System.out.println(stack.pop());
stack.push(12);
for (int i = 0; i < printables.length; i++)
printables[i].print();
Qu’écrit ce programme sur la sortie standard ?
Vérification des types à la compilation :
Stack[] arrayStack = new Stack[2];
arrayStack[0] = new PrintableStack();
arrayStack[1] = new PrintableString("t"); // Erreur !
PrintableString
n’implémente pas Stack
.
Le type Stack
n’est pas compatible avec le type Printable
.
Il est possible d’implémenter directement une interface dans le code :
public static void main(String[] args) {
String message = "Hello World!";
Printer printer = new Printer() {
// implémentation des méthodes de l'interface
public void print(String document) {
System.out.println("("+document+")");
}
};
printer.print(message);
}
Une telle classe est dite anonyme car on ne lui associe pas de nom.
Bien évidemment, la variable intermédiaire n’est pas nécessaire :
public static void main(String[] args) {
String message = "Hello World!";
(new Printer() {
// implémentation des méthodes de l'interface
public void print(String document) {
System.out.println("("+document+")");
}
}).print(message);
}
Classe anonymes : utiles dans certains cas comme pour décrire les actions suite à un événement dans une interface graphique.
Il est possible de forcer une conversion de type (transtypage) :
Printer printer = new SimplePrinter();
// → l'upcasting est toujours correct
// donc nous n'avons pas besoin d'opérateur.
SimplePrinter simplePrinter = (SimplePrinter) printer;
// → utilisation de l'opérateur de transtypage
// car nous ne faisons pas un upcasting.
Attention, un transtypage peut échouer (à l’exécution) :
L’utilisation du transtypage est souvent une erreur et est à éviter tant que possible.
\(\Rightarrow\) le transtypage peut échouer à l’exécution et donc créer des erreurs.
implémenter la méthode equals(Object obj)
\(\Rightarrow\) le paramètre est de type Object
(type de tous les objets en java) et il faut donc le convertir pour pouvoir le comparer.
réception d’objet depuis l’extérieur
\(\Rightarrow\) dans certains cas on crée des objets à partir de données externes (par le réseau ou via un fichier) et on doit donc les convertir pour les utiliser.
equals
Comment recycler tous les objets d’une poubelles
Avec le code suivant ?
Problème
Comment définir le tableau
Trashcan
?Quel est le type
T
de ces éléments ?Remarques
T
implémentent la méthoderecycle
Trashcan
doit pouvoir contenir des objets de types différents