400 likes | 536 Views
Interface utilisateur graphique en Java. Construire une interface graphique en Java (suite) adaptateurs d’événements et classes internes. actionPerformed(unEvnt ). JButton MonBouton. Source d’événements. Récepteur d’événements. unObjet. 2. Activer. getSource(). 3. 1.
E N D
Interface utilisateur graphique en Java Construire une interface graphique en Java (suite) adaptateurs d’événements et classes internes
actionPerformed(unEvnt ) JButton MonBouton Source d’événements Récepteur d’événements unObjet 2 Activer getSource() 3 1 new ActionEvent( ) ActionEvent unEvnt Modèle événementiel du JDK 1.1 • Le modèle événementiel de JDK 1.1 se compose : • d'objets sources d'événements • d'objets événements • d'objets récepteurs d'événements • Ces objets interagissent de façon standard en invoquant des méthodes pour permettre le déclanchement et la gestion des événements
1 addActionListener(unObjet) actionPerformed(unEvnt ) 2 JButton MonBouton Source d’événements Récepteur d’événements ... unObjet Activer actionPerformed(unEvnt ) 3 4 removeActionListener(unObjet) Sources et écouteursd’événements • Événements générés par des sources d’événements (« event sources ») • Source d’événements, un objet capable de : • déterminer quand un événement « intéressant » s'est produit • d'avertir (« notify ») des objets récepteurs (« event listeners ») de l'occurrence de cet événement • Pour être averti des événements produits par une source, un récepteur doit se faire enregistrer auprès de la source Implémente l’interface d’écoute ActionListener
JFrame JPanel EditeurGraphique BarreOutils 2 ZoneDessin 3 BarreEtat MouseEvent 1 • Type d’événement : MouseEvent (MOUSE_MOVED, MOUSE_DRAGGED) 1 • Source : la zone de dessin 2 • Récepteur : la zone de dessin également 3 Rappel Construction de l’interface graphique Gestion de l’interaction Déplacement de la souris sur la zone de dessin doit mettre à jour les coordonnées du curseur dans la barre d’état
L’objet zone graphique a connaissance de l’objet barre d’état pour pouvoir agir sur lui lors de la réception des MouseEvent rappel import javax.swing.*; public class EditeurGraphique extends JFrame { final static int HAUTEUR = 450; final static int LARGEUR = 750; public EditeurGraphique { BarreEtat barreEtat = new BarreEtat(); setTitle("Ma première Fenêtre"); setSize(LARGEUR,HAUTEUR); setJMenuBar(new MenuEditeur()); this.getContentPane().add(new BarreOutils(),"North"); this.getContentPane().add(new ZoneGraphique(barreEtat),"Center"); this.getContentPane().add(barreEtat,"South"); barreEtat.afficheInfo("coordonnées du cruseur"); setVisible(true); } public static void main(String[] args) { new EditeurGraphique(); } } // MyFrame • Code de la classe lançant l’application
import java.awt.*; import javax.swing.*; import java.awt.event.*; public class ZoneDessin extends JPanel implements MouseMotionListener { private BarreEtat be; public ZoneDessin(BarreEtat be) { setBackground(Color.white); setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); this.be = be; this.addMouseMotionListener(this); } public void mouseMoved(MouseEvent e) { be.afficheCoord(e.getX(),e.getY()); } public void mouseDragged(MouseEvent e) { be.afficheCoord(e.getX(),e.getY()); } } // ZoneGraphique L’objet zone dessin va être à l’écoute des événements MouseEvent de type MouseMotion 1 L’objet zone dessin s’enregistre lui-même comme récepteur des événements MouseEvent de type MouseMotion qu’il est susceptible de générer 3 L’objet zone dessin utilise les informations contenues dans l’objet MouseEvent qui lui est transmis pour mettre à jour la barre d’état. 2 rappel • Code de la classe représentant la zone graphique
Clic doit provoquer arrêt de l’application JFrame EditeurGraphique import javax.swing.*; public class EditeurGraphique extends JFrame { final static int HAUTEUR = 450; final static int LARGEUR = 750; public EditeurGraphique { setTitle("Ma première Fenêtre"); setSize(LARGEUR,HAUTEUR); setJMenuBar(new MenuEditeur()); this.getContentPane().add(new BarreOutils(),"North"); ... setVisible(true); } Adaptateurs d’événementsFermeture de la fenêtre Pas utilisable si on veut un comportement plus spécifique, par exemple ne quitter l'application que si l'utilisateur le confirme. this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); Il faut alors définir explicitement le code de gestion des événements sur la fenêtre
Clic doit provoquer arrêt de l’application JFrame EditeurGraphique 2 WindowEvent 3 1 • Type d’événement : WindowEvent (WINDOW-CLOSING) 1 • Source : l’éditeur graphique (JFrame) 2 • Récepteur : l’éditeur graphique 3 Adaptateurs d’événementsFermeture de la fenêtre
L’objet éditeur graphique va être à l’écoute des événements WindowEvent 1 L’objet éditeur graphique s’enregistre lui-même comme récepteur des WindowEvent qu’il est susceptible de générer 2 Adaptateurs d’événementsFermeture de la fenêtre import javax.swing.*; public class EditeurGraphique extends JFrame { final static int HAUTEUR = 450; final static int LARGEUR = 750; public EditeurGraphique { BarreEtat barreEtat = new BarreEtat(); setTitle("Ma première Fenêtre"); setSize(LARGEUR,HAUTEUR); setJMenuBar(new MenuEditeur()); this.getContentPane().add(new BarreOutils(),"North"); this.getContentPane().add(new ZoneGraphique(barreEtat),"Center"); this.getContentPane().add(barreEtat,"South"); barreEtat.afficheInfo("coordonnées du cruseur"); setVisible(true); } public static void main(String[] args) { new EditeurGraphique(); } } // MyFrame import java.awt.event.*; implements WindowListener { • Code de l’Editeur Graphique this.addWindowListener(this) Mais maintenant il faut aussi implémenter les méthodes de l’interface WindowListener
<interface> WindowListener WindowListener définit plusieurs méthodes, une pour chacun de ces types windowActivated(WindowEvent) Invoked when a window is activated windowClosed(WindowEvent) Invoked when a window has been closed. windowClosing(WindowEvent) Invoked when a window is in the process of being closed. windowDeactivated(WindowEvent) Invoked when a window is de-activated. windowDeiconified(WindowEvent) Invoked when a window is de-iconified. windowIconified(WindowEvent) Invoked when a window is iconified. windowOpened(WindowEvent) Invoked when a window has been opened. public class EditeurGraphique extends JFrameimplements WindowListener { public EditeurGraphique() { … this.addWindowListener(this); this.setVisible(true); } } public void windowActivated(WindowEvent e){;} ... public void windowOpened(WindowEvent e){} corps vide Adaptateurs d’événementsFermeture de la fenêtre WindowEvent définit plusieurs sous types d’événements WINDOW_ACTIVATED WINDOW_CLOSED WINDOW_CLOSING WINDOW_DEACTIVATED WINDOW_DEICONIFIED WINDOW_ICONIFIED WINDOW_OPENED setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE); Dans le cas présent seuls les événementsWINDOW_CLOSING nous intéressent. Mais quand on implémente une interface il faut implémenter toutes les méthodes qu’elle définit public void windowClosing(WindowEvent e){ if (JOptionPane.showConfirmDialog(this, "Voulez-vous vraimment quitter ?", null, JOptionPane.YES_NO_OPTION) == 0) System.exit(0); }
Étendre la classe WindowAdapter et n’implémenter que la méthode qui nous concerne 1 public class ClosingAdapter extends WindowAdapter { public void windowClosing(WindowEvent e){ if(JOptionPane.showConfirmDialog(this, "Voulez-vous vraiment quitter ?", null, JOptionPane.YES_NO_OPTION) == 0) System.exit(0); } } public class EditeurGraphique extends JFrame { public EditeurGraphique() { … this.addWindowListener( ); this.setVisible(true); } public static void main(String[] args){ new EditeurGraphique(); } } Associer une instance de cette classe à la fenêtre 2 Adaptateurs d’événementsFermeture de la fenêtre • Adaptateur d’événements WindowAdapter dans package java.awt.event new ClosingAdapter()
Appuyer sur un bouton de la souris (MOUSE_PRESSED) définit le début d’une droite Relâcher le bouton de la souris (MOUSE_RELEASED) définit la fin de la droite • type d’événement : MouseEvent • source : zone de dessin • interface d’écoute : MouseListener • récepteur : zone de dessin < interface > MouseListener void mouseClicked(MouseEvent) void mouseEntered(MouseEvent) void mouseExited(MouseEvent) void mousePressed(MouseEvent) void mouseReleased(MouseEvent) Comme pour la fermeture de la fenêtre, seules deux des méthodes de l’interface nous intéressent Adaptateurs d’événementsDessin des segments de droite Déplacement de la souris sur la zone de dessin met à jour les coordonnées du curseur dans la barre d’état MOUSE_MOVED, MOUSE_DRAGGED • Gestion des événements souris
import java.awt.*; import javax.swing.*; import java.awt.event.*; public class ZoneDessin extends JPanel implements MouseMotionListener { private BarreEtat be; public ZoneDessin(BarreEtat be) { setBackground(Color.white); setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); this.be = be; addMouseMotionListener(this); } public void mouseMoved(MouseEvent e) { be.afficheCoord(e.getX(),e.getY()); } public void mouseDragged(MouseEvent e) { be.afficheCoord(e.getX(),e.getY()); } } // ZoneDessin import java.awt.event.*; public class GestionnaireClic extends MouseAdapter { public void mousePressed(MouseEvent e) { zone.initieDroite(e.getX(),e.getY()); } public void mouseReleased(MouseEvent e) { zone.termineDroite(e.getX(),e.getY()); } } Adaptateurs d’événementsDessin des segments de droite Pour ne pas avoir à définir des méthodes inutiles, possibilité d’utiliser un adaptateur d’événements : MouseAdapter addMouseListener(new GestionnaireClic(this)); private ZoneDessin zone; public GestionnaireClic(ZoneDessin z) { zone = z; } public void initieDroite(int x, int y) { be.afficheMessage(« Relacher pour dessiner la droite »); // on complétera ensuite } public void termineDroite(int x, int y) { be.afficheMessage(« Cliquer pour initier une droite »); // on complétera ensuite }
Annule le dernier tracé Efface toute la zone de dessin Des adaptateursaux classes internes • Gestion des boutons de la barre d’outil • type d’événement : ActionEvent • source : les JButton • interface d’écoute : ActionListener • récepteur ? L’activation des boutons doit se traduire par un changement d’état de la zone de dessin. Il semble naturel que cela soit la zone dessin qui récupère les événements et réalise les traitement adéquats.
2) La zone dessin reçoit les événements issus des boutons , ActionListener { le code zone dessin dépend du code de la barre d’outils. 1) C’est la zone dessin qui effectue les traitements déclenchés par les boutons efface et annule public void actionPerformed(ActionEvent e) { selon l’origine de l’événement efface() ou annule() } Des adaptateursaux classes internes import java.awt.*; import javax.swing.*; import java.awt.event.*; public class ZoneDessin extends JPanel implements MouseMotionListener { private BarreEtat be; public ZoneDessin(BarreEtat be) { …. } public void mouseMoved(MouseEvent e) { ... } public void mouseDragged(MouseEvent e) { ... } public void initieDroite(int x, int y) { ... } public void termineDroite(int x, int y) { ... } } // ZoneGraphique • Quid si on supprime la barre d’outils et que toutes les opérations s’effectuent via la barre de menus ? • Lourdeur du code : • si beaucoup de boutons à écouter • si plusieurs moyens de déclencher ces actions (boutons, menus…) public void efface() { ... } public void annule() { ... }
Des adaptateursaux classes internes • Le modèle événementiel de JAVA 1.1 peut rapidement devenir difficile à gérer • si un objet doit être à l’écoute d’un grand nombre d’objets sources, • il lui faut implémenter les interfaces appropriées à chacun de ces objets • si un objet est à l’écoute d’événements issus de deux objets (ou davantage) sources d'événements du même type • l’objet récepteur ne peut implémenter qu’une seule fois l’interface réceptrice d’événements, il lui faudra découvrir lui-même quel objet a émis l’événement • Cela peut conduire à du code difficile à lire et/ou difficile à écrire • Une solution : introduire un objet « médiateur » entre la source d’événements et le récepteur d’événements • son rôle : adapter la source aux besoins spécifiques d’où le nom d’adaptateur d’événements
import java.awt.event.*; import java.awt.*; public class AdaptateurEfface implements ActionListener { ZoneDessin zone; public adaptateurEfface(ZoneDessin z) { zone = z; } public void actionPerformed(ActionEvent e) { zone.efface(); } } Dans le code de BarreOutils ... BarreOutils(ZoneDessin zd) { ... JButton b; this.add(b= new JButton("Défaire")); b.addActionListener(new AdaptateurAnnule(zd)); this.add(b = new JButton("Tout effacer")); b.addActionListener(new AdaptateurEfface(zd)); ... } ... Adaptateurs d’événements import java.awt.event.*; import java.awt.*; public class AdaptateurAnnule implements ActionListener { ZoneDessin zone; public AdaptateurEfface(ZoneDessin z) { zone = z; } public void actionPerformed(ActionEvent e) { zone.annule(); } } Des adaptateursaux classes internes • Simplification de ZoneDessin • Meilleur découplage des différents éléments de l’interface utilisateur • Mais • multiplication du nombre de classes • classes pas vraiment réutilisables
Des adaptateursaux classes internes • Alors que faut-il faire ? • classes imbriquées (« nested classes ») et internes (« inner classes ») • possibilité de définir des classes à l’intérieur d’autres classes • l’une des évolutions majeures de Java 1.1 • si on élimine « les cas obscurs et pathologiques » (D. Flanagan in Java in a nutshell, 2nd Edition, O ’Reilly) c’est un ajout élégant et très utile • utilisation très fréquente en particulier avec le nouveau modèle d’événements introduit aussi dans Java 1.1
class X { class Member extends Superclass {…} } class X { void work() { class Local extends Superclass {…} } } class X { void work() { obj =new Superclass() {…} } } class Anonymous extends/implements Superclass Classes internes • Classe membre : définition de la classe à l’intérieur d’une classe, au même niveau que les attributs et méthodes • Trois types de classes internes : • Classe locale : définition de la classe à l’intérieur d’une méthode • Classe anonyme : définition de la classe à l’intérieur d’une expression
class X { class Member extends Superclass {…} } public class LinkedList { private Linkable head; ... public void insert(Linkable node) { ... } public void remove(Linkable node) { ... } } public interface Linkable { public Linkable getNext(); public void setNext(Linkable node); } public interface Iterator { public boolean hasNext(); public Object next(); } Classes internesclasses membres • Toute instance d’une classe membre est associée de manière interne à une instance de la classe englobante • accès implicite aux membres (attributs/méthodes) définis dans la(les) classe(s) englobante(s), (y compris les membres privés) • exemple : (d’après Java In a Nutshell, 2nd Edition, D. Flanagan, O ’Reilly) • Classes membres Implémentation d’une liste chaînée Ajouter à la liste chaînée la possibilité de parcourir les éléments à l’aide d’un Iterator public Iterator iterator() { ... }
Nécessité de passer explicitement un objet LinkedList dans le constructeur et de rendre accessible l'attribut head public class LinkedList { private Linkable head; ... public void insert(Linkable node) { ... } public void remove(Linkable node) { ... } public Iterator iterator() { ... } } public class LinkedList { private Linkable head; ... public void insert(Linkable node) { ... } public void remove(Linkable node) { ... } public Iterator iterator() { ... } } Plus besoin de passer l’objet LinkedList dans le constructeur return new LinkedListIterator(this); class LinkedListIterator implements Iterator { private Linkable current; public boolean hasNext() { return current != null; } public Object next() { if(current == null) throw NoSuchElementException("LinkedList"); Object value = current; current = current.getNext(); return value } } head; public LinkedListIterator(LinkedList l) { } current = l.head; Accès direct aux membres (même privés) de la classe englobante Classes internesclasses membres Sans classe membre Avec une classe membre return new LinkedListIterator(); private class LinkedListIterator implements Iterator{ private Linkable current; public LinkedListIterator() { current = head; } public boolean hasNext() { return current != null; } public Object next() { if(current == null) throw NoSuchElementException(«LinkedList»); Object value = current; current = current.getNext(); return value } }
public class LinkedList { private Linkable head; ... public void insert(Linkable node) { ... } public void remove(Linkable node) { ... } public Itrator iterator() { return new LinkedListIterator(); } } (this); 3 le compilateur insère auto-matiquement un attribut privé pour contenir cette référence 1 private LinkedList container; (LinkedList l) { 1 2 le compilateur ajoute auto-matiquement un argument caché à tous les constructeurs de la classe membre 2 et passe l’objet contenant en valeur de cet argument 3 Classes internesclasses membres • Toute instance d’une classe membre est associée de manière interne à une instance de la classe englobante • Comment cela marche ? private class LinkedListIterator implements Iterator{ private Linkable current; public LinkedListIterator() { current = head; } public boolean hasNext() { return current != null; } public Object next () { if(current == null) throw NoSuchElementException(«LinkedList»); Object value = current; current = current.getNext(); return value } } container = l;
this.current = this.head; this.current = LinkedList.this.head; Pas d’attribut head dans la classe membre Syntaxe pour faire référence explicitement à l’instance contenante de l’instance courante de la classe membre Classes internesclasses membres public class LinkedList { private Linkable head; ... } private class LinkedListIterator implements Iterator { private Linkable current; public LinkedListIterator() { current = head; } ... } • Accès implicite aux champs (attributs/méthodes) définis dans la(les) classe(s) englobante(s), (y compris les champs privés)
import java.util.*; public class C { Vector a; ... } public class A { int a; } class B extends C { } En cas de conflit, le compilateur le signale : Variable 'a' is inherited in inner class A.B, and hides a variable of the same name in class A. An explicit 'this' qualifier must be used to select the desired instance. this.a est un Vector A.this.a est un int Classes internesclasses membres • Hiérarchie d’imbrication et hiérarchie d’héritage sont totalement distinctes l’une de l’autre • héritage : définit les champs (attributs, méthodes) dont hérite la classe membre • imbrication : définit les champs qui sont dans la portée (accessibles) de la classe membre. a ?? • Restrictions sur les classes membres • ne peuvent avoir un nom identique à celui de l’une des classes englobantes • ne peuvent contenir de membres (attributs, méthodes ou classe) static
public class LinkedList { } class LinkedListIterator { } LinkedList.class javac LinkedList.java LinkedList$LinkedListIterator.class Classes internesclasses membres • Compilation de classes membres • La machine virtuelle JAVA n’a pas été modifiée pour la prise en compte des classes internes • Le compilateur doit convertir les classes internes en classes standard (top-level classes) que l’interpréteur JAVA peut comprendre • C’est effectué à l’aide de transformations du code source (insertion ‘ $ ’ dans le nom des classes internes, insertion d’attributs et paramètres cachés...).
class X { void work() { class Local extends Superclass {…} } } Classes internesclasses locales • Une classe locale est une classe interne, elle partage bien des caractéristiques des classes membres. • Une classe locale est aux classes membres ce qu’est une variable locale aux variables d’instance • uniquement visible et utilisable dans le bloc de code où elle est définie • elle peut utiliser n’importe quelle variable locale (ou paramètre de méthode) visible à l’endroit où elle est définie à condition qu’elle (ou qu’il) soit déclaré final • exemple : (toujours d’après Java In a Nutshell, 2nd Edition, D. Flanagan, O ’Reilly) • Classes locales : définies à l’intérieur d’un bloc de code
Classe membre Classe locale public class LinkedList { private Linkable head; ... public void insert(Linkable node) { ... } public void remove(Linkable node) { ... } public Iterator iterator() { } public class LinkedList { private Linkable head; ... public void insert(Linkable node) { ... } public void remove(Linkable node) { ... } public Iterator iterator() { return new LinkedListIterator(); } } class LinkedListIerator implements Iterator { private Linkable current; public LinkedListIterator() { current = head; } public boolean hasMoreElements() { return current != null; } public Object nextElement() { if(current == null) throw NoSuchElementException(«LinkedList»); Object value = current; current = current.getNext(); return value } } private class LinkedListIterator implements Iterator { private Linkable current; public LinkedListIterator() { current = head; } public boolean hasNext() { return current != null; } public Object next () { if(current == null) throw NoSuchElementException(«LinkedList»); Object value = current; current = current.getNext(); return value } } La définition de la classe est encore rapprochée de l’endroit où elle est utilisée -> augmente la clarté du code (?) Classes internesclasses locales return new LinkedListIterator(); }
public class A { private int a; ... public void methodeX(int param1, int param2){ int local1; double local2; local2 = ... ; ... new B() ... } // fin methode x ... } class B { private int b; public B() { ... } public void methodY() { a = ... ; } ... } // fin classe B Classes internesclasses locales javac ClasseA.java A.java:13: local variable param2 is accessed from within inner class; needs to be declared final if (param2 == 0 && local2 < 0.0) { ^ A.java:13: local variable local2 is accessed from within inner class; needs to be declared final if (param2 == 0 && local2 < 0.0) { ^ 2 errors Compilation exited abnormally with code 1 at Mon Mar 18 07:22:02 • Ce qui différencie une classe locale d’une classe membre est qu’elle a en plus accès aux variables locales (et paramètres) visibles dans son bloc de définition. if (param2 == 0 && local2 < 0.0) { ... } Pourquoi ?
Une classe locale peut accéder aux variables locales car le compilateur ajoute automatiquement : public class A { private int a; ... public void methodeX(int param1, int param2){ int local1; double local2; local2 = ... ; ... new B() ... } // fin methode x ... } final • des attributs privés pour stocker une copie des variables locales final 1 class B { private int b; public B() { ... } ... } private int attributCaché1; private double attributCaché2; 1 • des paramètres cachés aux constructeurs de la classe membre pour initialiser ces attributs privés créés automatiquement. (private int pCaché1, double pCaché2) { 2 2 B(param2,local2) Classes internesclasses locales • Ce qui différencie une classe locale d’une classe membre est qu’elle a en plus accès aux variables locales (et paramètres) visibles dans son bloc de définition. • Mais à condition que ces variables et paramètres soient déclarés final • Comment et pourquoi ? Le seul moyen de maintenir ces copies en accord avec les variables locales est de définir ces dernières comme finales
Classes internesclasses locales • Restrictions sur les classes locales • comme les classes membres elles ne peuvent contenir de champs (attributs ou méthodes) static • comme les classes membres elles ne peuvent avoir le même nom que l’une des classes englobantes • elles ne peuvent être déclarées public, protected, private ou static (de la même manière que ces modifieurs ne sont pas autorisés pour des variables locales)
Annule le dernier tracé Efface toute la zone de dessin • type d’événement : ActionEvent • source : les JButton • interface d’écoute : ActionListener Classes internesclasses locales • Une utilisation très courante des classes locales est pour la réalisation d’adaptateurs d’événements • Reprenons la gestion des boutons de la barre d’outils de l’éditeur graphique Récepteur : utilisation d’une classe locale dans le constructeur de la barre d’outils
import java.awt.event.*; import java.awt.*; import java.swing.*; public class BarreOutils extends JPanel { public BarreOutils( ZoneDessin zd) { ... JButton bDefaire = new JButton("Défaire"); this.add(bDefaire); JButton bEffacer = new JButton("Tout effacer"); this.add(bEffacer); ... ... } final final Variables déclarées finales pour les rendre accessibles dans le code de la classe interne Classe interne adaptateur d’événements final class BOActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource() == bDefaire) zd.defaire(); else if (e.getSource() == bEffacer) zd.efface(); } } Création d’une instance de la classe interne qui est à l’écoute des boutons BOActionListener boAL = new BOActionListener(); bDefaire.addActionListener(boAL); bEffacer.addActionListener(boAL); Des adaptateursaux classes internes Dans le code de BarreOutils
A.java public class A { void methodA() { } void methodB() { } } A.class A$1$B.class javac A$2$B.class Classes internesclasses locales class B { } • Compilation de classes locales class B extends C { }
class X { void work() { obj =new Superclass() {…} } } class Anonymous extends/implements Superclass Classes internesclasses anonymes • Classes locales : définies à l’intérieur d’un bloc de code • Une classe anonyme est une classe locale sans nom • Permet de regrouper définition et instanciation d’une classe locale • Très couramment utilisées en tant qu’adaptateurs d’événements
Avec des classes anonymes import java.awt.event.*; import java.awt.*; Import java.swing.*; public class BarreOutils extends JPanel { public BarreOutils(final ZoneDessin zd) { ... JButton bDefaire = new JButton("Défaire"); this.add(bDefaire); JButton bEffacer = new JButton("Tout effacer"); this.add(bEffacer); ... bDefaire.addActionListener( ); bEffacer.addActionListener( ); ... } new ActionListener() { public void actionPerformed(ActionEvent e) { zd.defaire(); } } Classe anonyme adaptateur d’événements pour le JButton Classes internesclasses anonymes Avec une classe locale Import java.swing.*; import java.awt.event.*; import java.awt.*; public class BarreOutils extends JPanel { public BarreOutils(final ZoneDessin zd) { ... final JButton bDefaire = new JButton("Défaire"); this.add(bDefaire); final JButton bEffacer = new JButton("Tout effacer"); this.add(bEffacer); ... BOActionListener boAL = new BOActionListener(); bDefaire.addActionListener(boAL); bEffacer.addActionListener(boAL); ... } class BOActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource() == bDefaire) zd.defaire(); else if (e.getSource() == bEffacer) zd.efface(); } } new ActionListener() { public void actionPerformed(ActionEvent e) { zd.efface(); } }
Classes internesclasses anonymes new NomDeClasse() { corps de la classe } ( liste d’arguments ) { new NomDinterface() { corps de la classe } classe anonyme sous-classe de la classe NomDeClasse classe anonyme implémente l’interface NomDinterface dans ce cas la classe anonyme est sous-classe de Object Une classe anonyme n’ayant pas de nom il n’est pas possible de définir de constructeur dans son corps Les arguments éventuellement spécifiés après NomDeClasse sont implicitement passés en paramètre du constructeur de cette super-classe Jamais d’arguments après NomDinterface puisque le constructeur de la super-classe (Object) n’a pas d’arguments
public class EditeurGraphique extends JFrame { public EditeurGraphique() { ... this.addWindowListener( ); this.setVisible(true); } public static void main(String[] args) { new EditeurGraphique(); } } new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } public class ClosingAdapter extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public class EditeurGraphiqueextends JFrame implements WindowListener { public EditeurGraphique() { ... this.addWindowListener(new ClosingAdapter()); this.setVisible(true); } public static void main(String[] args) { new EditeurGraphique(); } } Classes internesclasses anonymes avec classe interne anonyme sans classe interne
Classes internesclasses anonymes • Comment choisir entre écrire une classe locale ou écrire une classe anonyme • choisir ce qui rend le code plus clair • Classe anonyme • la classe doit avoir un corps petit • une seule instance de la classe est nécessaire • la classe est utilisée juste après qu’elle soit définie • le nom éventuel de la classe ne rend pas le code plus facile à comprendre • Classe locale • la classe doit avoir plusieurs instances • la classe nécessite un constructeur
A.java public class A { } A.class A$1.class javac A$2.class Classes internesclasses anonymes new B() { } • Compilation de classes anonymes new C() { }