Une classe déclarée avec le modificateur final ne peut pas être dérivée.
/** * Exemple de classe final non dérivée * @author phil * */ public final class Unique { private String nom; public Unique(String nom) { this.nom=nom; } } class PasUnique extends Unique { // bah non ! c'est pas possible ! }
Exemple avec une classe Programmeur définie comme final :
final class Programmeur extends Employe
Une méthode déclarée avec le modificateur final ne peut pas être réécrite dans une classe dérivée :
class Employe { private String prenom; /** * Méthode qui sera impossible de réécrire dans une sous-classe. * Essayez * @return */ final public String getPrenom() { return prenom; } } class JeBosse extends Employe { /** * Impossible ! vous aurez le message "Cannot override the final method from Employe" */ public String getPrenom() { } }
Dans une hiérarchie de classes, on peut mettre en facteur commun des attributs et des méthodes.
Ci-dessous, la classe Contour servira de classe de base à un ensemble de sous-classes du genre Cercle, Rectangle et autres formes.
Nous allons donc par la suite la rendre abstraite puisque nous n’avons aucune raison de l’instancier !
/** * Classe générique non abstraite */ class Contour { protected int x , y ; // pour l'héritage public Contour(int x, int y) { this.x=x; this.y=y; } public void deplace( int x , int y) { // efface(); // écriture illogique car on ne sait pas quoi effacer ! this.x = x ; this.y = y ; // dessine(); // écriture illogique car on ne sait pas quoi dessiner ! } }
Selon vous, peut-on écrire les méthodes efface() et dessine() de cette manière ?
Est-il raisonnable de créer un objet Contour ? Je vous donne la réponse : NON
NON
Voici la manière d’interdire l’instanciation d’une classe Contour avec l’utilisation du mot-clé abstract :
/** * Classe générique devenue abstraite ! */ public abstract class Contour { protected int x , y ; // pour l'héritage public Contour(int x, int y) { this.x=x; this.y=y; } public void deplace( int x , int y) { // efface(); // écriture illogique car on ne sait pas quoi effacer ! this.x = x ; this.y = y ; // dessine(); // écriture illogique car on ne sait pas quoi dessiner ! } // voici comment on déclare des méthodes abstraites (comme dans une interface ;) ) public abstract void dessine(); public abstract void efface(); }
Ci-dessus, la classe Contour on ne peut pas écrire le code des méthodes efface() et dessine().
Comment faire figurer les signatures des méthodes dans la classe Contour sans fournir de code ?
Il suffit de les déclarer abstraites avec le mot clé abstract :
abstract public void efface(); // ou public abstract void dessine(); abstract public void dessine(); // public abstract void efface();
Il faut donc hériter de la classe Contour pour utiliser deplace() et fournir le code des méthodes efface() et dessine() que je définie comme abstract. Si vous vous souvenez de l’utilisation d’une interface en java, et bien les méthodes sont déclarées sans code comme ici !
Vous pouvez utiliser les syntaxes suivantes :
//======== classe dérivée Cercle class Cercle extends Contour { private int rayon; public Cercle(int x, int y, int rayon) { super(x, y); // appel du constructeur de la super classe this.rayon = rayon; } public void dessine() { // votre code pour dessiner un cercle } public void efface() { // votre code pour effacer un cercle } } // classe dérivée Rectangle class Rectangle extends Contour { private int hauteur; private int largeur; public Rectangle(int x, int y, int hauteur, int largeur) { super(x, y); // appel du constructeur de la super classe this.hauteur = hauteur; this.largeur = largeur; } public void dessine() { // votre code pour dessiner un rectangle } public void efface() { /* votre code pour effacer un rectangle g.setColor(Color.WHITE); g.fillRect(x, y, width, height); g est un objet de type Graphics qui permet de dessiner des formes sur un composant graphique x, y, width, height sont les coordonnées et les dimensions du rectangle. */ } }
Pour résumer, une classe abstraite peut contenir :
IMPORTANT : Si on déclare une méthode abstraite dans une classe, cette classe doit être déclarée abstraite.
Quelques règles de codage pour les méthodes abstraites :
Au sens java, une interface est :
Une classe abstraite dont toutes les méthodes sont abstraites.
Elle présente un plus haut degré d’abstraction qu’une classe abstraite et permet de définir un contrat entre les classes.
On appelle généralement les méthodes de l’interface depuis d’autres classes.
Une autre classe devra écrire le code pour les méthodes définies dans l’interface.
IMPORTANT : Une interface est un type au même titre qu’une classe (Vous découvrirez ultérieurement l’utilité de ce type particulier)
Il existe plusieurs sens du terme interface :
Comme une interface constitue un type, on peut déclarer une référence du type de l’interface. Pour déclarer une Interface, on remplace le mot class par interface.
Dans une interface, toutes les méthodes sont abstraites. Pas besoin d’ajouter le mot clé abstract
Exemple :
interface Executable { public void execute( ); }
La classe qui fournit le code est dérivée de l’interface en utilisant le mot clef implements
La classe doit fournir le code pour les méthodes définies dans l’interface comme ceci :
class Traitement implements Executable { public void execute( ) { System.out.println("Je suis la méthode execute de Traitement !"); } }
Une autre Classe qui utilise la même interface :
class AutreTraitement implements Executable { @Override public void execute() { System.out.println("Je suis une autre implémentation de execute (AutreTraitement) !"); } }
La classe qui va utiliser le code aura une méthode attendant un objet du type de l’interface.
class Gestionnaire // le code de cette classe n'a pas besoin d'être modifiée, on utilise le type Executable (l'interface) public void aFaire( Executable tache ) { // Appel du code écrit dans la classe Traitement ou AutreTraitement ou autre... tache.execute( ) ; } public static void main(String args[]) { Gestionnaire g = new Gestionnaire( ); Traitement t = new Traitement(); // est une classe de type Executable AutreTraitement at = new AutreTraitement(); // idem g.aFaire( t ); g.aFaire( at ); }
Résultat dans la console :
Je suis la méthode execute de Traitement ! Je suis une autre implémentation de execute !
La classe Gestionnaire ne connaît que l’interface Executable ! Elle ignore la classe Traitement qui est finalement du type Executable.
Dans cet exemple, nous allons voir l’utilisation de l’interface Comparable (java.lang.Comparable) qui permet de déterminer par programmation le classement d’une série d’objets stockés dans une collection de type TreeSet.
L’implémentation de cette interface nous oblige à redéfinir une méthode compareTo() qui sera automatiquement appelée par notre collection TreeSet lors de l’affichage des objets.
public int compareTo(Object objet)
La valeur retournée est :
0 : si objet en cours (this) > paramètre (idem)
Voici la classe Voiture qui implémente notre interface Comparable :
package interfaceComparableEtTreeSet; /** * Utilisation de l'interface java.lang.Comparable * Qui permet de comparer deux objets en fonction * de critères définis par le/la dev. */ public class Voiture implements Comparable<Voiture> { private String numero; public String modele; public Voiture (String numero, String modele) { // ici, j'utilise Optional pour éviter des "NullPointerException" // pour la méthode comparableTo c'est mieux ! this.numero = Optional.ofNullable(numero).orElse(""); this.modele = Optional.ofNullable(modele).orElse(""); /* nous pourrions aussi utiliser requireNonNull() this.numero = Objects.requireNonNull(numero, "Le numéro ne peut pas être null !"); this.modele = Objects.requireNonNull(modele, "Le modèle ne peut pas être null !"); */ } public String getModele() { return this.modele;} public String getNumero() { return this.numero;} public void setModele(String modele) { this.modele = modele;} public void setNumero(String numero) { this.numero = numero;} public String toString() { return "Voiture numero : " + numero + " Modèle : " + modele; } /* Implémenter la méthode public int compareTo(Object param) de l'interface java.lang.Comparable et comparer les objets en effectuant la concaténation des textes modele+numero */ @Override public int compareTo(Voiture voiture) { String voitureEnCours = this.modele + this.numero; String autreVoiture = voiture.getModele() + voiture.getNumero(); return voitureEnCours.compareTo(autreVoiture); } }
Voici la classe TreeSet qui va nous permettre de stocker les objets de type Voiture :
package interfaceComparableEtTreeSet; import java.util.Iterator; import java.util.TreeSet; //importez le package ou se trouve la classe TreeSet. /** * * Utilisation de la classe Voiture qui implémente * l'interface Comparable. */ public class TestTreeSet { public TestTreeSet() { TreeSet<Voiture> ts = new TreeSet<Voiture>(); ts.add(new Voiture( "468 BHY 75","Peugeot")); ts.add(new Voiture( "315 VV 78","Renault")); ts.add(new Voiture("201 ABC 22","Audit")); Iterator<Voiture> it = ts.iterator(); int ordre = 1; while(it.hasNext()) { System.out.printf("%d : %s\n", ordre, it.next()); ordre++; } /* Remarques : La voiture "201 ABC 22 est un modèle : Audit" a été placée en première position alors que nous l'avons ajouté en dernier. Ceci grâce à la méthode compareTo() que nous avons défini dans la classe Voiture qui implémente l'interface Comparable. */ public static void main (String[] arg) { new TestTreeSet(); } }
Exécutez ce programme et vérifiez l’ordre d’affichage des objets Voiture dans la console !
Résultat attendu :
1 : Voiture numero : 201 ABC 22 Modèle : Audit 2 : Voiture numero : 468 BHY 75 Modèle : Peugeot 3 : Voiture numero : 315 VV 78 Modèle : Renault