Théorie 3.1: structure générique (1) #
-
Comment définir un tableau générique contenant des méthodes comme
valeurMinimale
(le plus petit élément du tableau)trierTableau
-
Comment s’assurer que le même code fonctionne pour:
- tableau d’entiers, tableau de chaînes, tableau de
Vehicule
, etc.
- tableau d’entiers, tableau de chaînes, tableau de
-
On regarde deux techniques ici:
- Définir un tableau d’objets
Object[] tableau;
- Définir un tableau d’objets comparables
Comparable[] tableau;
- Définir un tableau d’objets
Première version: Object[] tableau
#
-
En java, une classe hérite toujours de
Object
-
Un tableau
Object[] tableau
peut alors stoquer n’importe quel sorte d’objet -
Voici l’interface de la première version de notre
Tableau
générique:
public interface Tableau {
Object obtenirValeur(int index);
void modifierValeur(int index, Object nouvelleValeur);
Object valeurMinimale();
}
- Le
Tableau
peut faire trois choses:obtenirValeur
: récupérer la valeur stoquée à un certainindex
modifierValeur
: stoquer une valeur à un certainindex
valeurMinimale
: trouver et retourner la plus petite valeur du tableau
Implanter Object[] tableau
#
- L’implantation commence comme suit:
public class MonTableau implements Tableau {
private Object[] valeurs;
public MonTableau(Object[] valeurs) {
this.valeurs = valeurs;
}
}
-
Le constructeur permet de spécifier les valeurs initiales (un tableau d'
Object
)- ces valeurs sont mémorisées dans un attribut privé
-
Le tableau est générique. On peut créer plusieurs types de tableaux:
public static void main(String[] args) {
MonTableau tableauEntiers = new MonTableau(new Integer[] {54,1,5,43,6});
MonTableau tableauChaines = new MonTableau(new String[] {"25","45","4","5"});
MonTableau tableauVehicules = new MonTableau(new Vehicule[] {new Auto(13.0), new Moto(23.9), new Camion(134.0)});
}
- L’implantation de
modifierValeur
etobtenirValeur
est simple:
@Override
public Object obtenirValeur(int index) {
return valeurs[index];
}
@Override
public void modifierValeur(int index, Object nouvelleValeur) {
valeurs[index] = nouvelleValeur;
}
- L’utilisation aussi:
int entier = tableauEntiers.obtenirValeur(1);
tableauEntiers.modifierValeur(1, entier+10);
String chaine = tableauChaines.obtenirValeur(3);
tableauChaines.modifierValeur(0,"Ah!");
Vehicule vehicule = tableauVehicules.obtenirValeur(0);
tableauVehicules.modifierValeur(2, new Auto(21.0));
- L’implantation de
valeurMinimale
est à premier vue simple:
@Override
public Object valeurMinimale() {
Object valeurMinimale = null;
if(valeurs.length > 0) {
valeurMinimale = valeurs[0];
}
for(int i = 1; i < valeurs.length; i++) {
if(siValeurPlusPetite(valeurs[i], valeurMinimale)) {
valeurMinimale = valeurs[i];
}
}
return valeurMinimale;
}
}
- Le problème est dans l’implantation de
siValeurPlusPetite
Problème avec Object[] tableau
#
- Comparer les valeurs est un problème:
private boolean siValeurPlusPetite(Object valeur, Object valeurMinimale) {
boolean siValeurPlusPetite = false;
if(valeur instanceof Integer && valeurMinimale instanceof Integer) {
siValeurPlusPetite = siEntierPlusPetit((Integer) valeur, (Integer) valeurMinimale);
}else if(valeur instanceof String && valeurMinimale instanceof String) {
siValeurPlusPetite = siChainePlusPetite((String) valeur, (String) valeurMinimale);
}else if(valeur instanceof Vehicule && valeurMinimale instanceof Vehicule) {
siValeurPlusPetite = ((Vehicule)valeurMinimale).siMoinsDeKilometrageQue((Vehicule)valeurMinimale);
}else {
// ?????
}
return siValeurPlusPetite;
}
-
Il faudrait ajouter du code à la méthode ci-haut pour chaque type d’objet
-
Notre classe
Tableau
n’est plus vraiment générique -
La solution est de déléguer aux objets la tâche de se comparer
-
On a besoin pour ça d’un contrat avec ces objets
Deuxième version: Comparable[] tableau
#
-
L’interface
Comparable
est un contrat qui signifie qu’un objet sait se comparer -
Pour remplir ce contrat, il faut implanter la méthode
int compareTo(Object autre)
- retourner
-1
si l’objet courant est plus petit que l'autre
objet - retourner
0
si l’objet courant est égal à l'autre
objet - retourner
+1
si l’objet courant plus grand que l'autre
objet
- retourner
-
Avec
Comparable
, l’interface de notre tableau générique devient:
public interface Tableau {
Comparable obtenirValeur(int index);
void modifierValeur(int index, Comparable nouvelleValeur);
Comparable valeurMinimale();
}
* Dans Eclipse, vous allez avoir des avertissements de *type incomplet*
* On peut les ignorer pour l'instant
<center>
<video width="50%" src="avertissements.mp4" type="video/mp4" controls playsinline>
</center>
- L’implantation commence comme suit:
public class MonTableau implements Tableau {
Comparable[] valeurs;
@Override
public void valeursInitiales(Comparable[] valeurs) {
this.valeurs = valeurs;
}
-
Cette fois-ci, on mémorise un tableau de
Comparable
- on ne connaît rien des objets, sauf qu’ils implante
compareTo(Object autre)
- on ne connaît rien des objets, sauf qu’ils implante
-
Les méthodes
obtenirValeur
,modifierValeur
etvaleurMinimale
sont pareilles -
La méthode
siValeurPlusPetite
n’est plus du tout problématique
private boolean siValeurPlusPetite(Comparable valeur, Comparable valeurMinimale) {
return valeur.compareTo(valeurMinimale) < 0;
}
- Par contre, il faut maintenant que
Vehicule
implanteComparable
public abstract class Vehicule implements Comparable {
private double totalKilometres = 0;
public Vehicule(double totalKilometres) {
this.totalKilometres = totalKilometres;
}
@Override
public int compareTo(Object autre){
int resultat = 0;
if(this.totalKilometres < ((Vehicule) autre).totalKilometres){
resultat = -1;
}else if(this.totalKilometres > ((Vehicule) autre).totalKilometres){
resultat = +1;
}
return resultat;
}
//...
}
- NOTE: les classes
String
etInteger
(et d’autres) implantent déjàComparable
Recapitulation #
-
Pour créer un tableau générique, il faut mémoriser des objets qu’on ne connaît pas
-
Pour comparer ces objets, il faut exiger qu’ils implantent l’interface
Comparable
-
Pour en faire plus, il faut exiger une interface avec plus de méthodes
-
Par exemple, si on veut que
Tableau
affiche les valeurs, on peut demander:- que chaque objet implante
ElementTableau
qui contientString formater()
- alors on a:
- que chaque objet implante
public class MonTableau implements Tableau, Formateur {
ElementTableau[] valeurs;
//...
@Override
public String formater(){
StringBuilder builder = new StringBuidler();
builder.append("[");
if(valeurs.length > 0){
builder.append(valeurs[0].formater());
}
for(int i = 1; i < valeurs.length; i++) {
builder.append(", ");
builder.append(valeurs[i].formater());
}
builder.append("]");
return builder.toString();
}
}
Problèmes avec Comparable[] tableau
#
-
Notre
Tableau
ne permet pas vraiment de spécifier le type des éléments -
En particulier, on peut faire:
public static void main(String[] args) {
MonTableau tableauChaines = new MonTableau(new String[] {"25","45","4","5"});
// Permis?
tableauChaines.modifierValeur(0, 12);
tableauChaines.modifierValeur(0, new Auto(12.9));
// Oups, erreur d'exécution
String chaine = (String) tableauChaines.valeurMinimale();
}
-
On peut ajouter un entier ou une
Auto
à un tableau de chaînes? -
Comment bloquer cette option pour éviter les erreurs d’exécution?
-
Réponse ici: structures génériques (2)