Héritage presque multiple en Java (1/2) Utiliser deux classes ou plus dans la définition d'une nouvelle classe peut se faire par composition. class Etudiant{ int numero; Diplome d; float passeexamen(examen e){ // retourne la note class Fetard{ void participefete(fete f){ class EtudiantFetard extends Etudiant{ Fetard f; float participefete(fete f){ return this.f.participefete(f); 1
Héritage presque multiple en Java (2/2) Java propose cependant une autre solution : les interfaces. L'héritage multiple pose des problèmes de polymorphisme en cas de résolution dynamique. class Chose{ // attributs interface Bidule Chose{ b; // // methodes void void m(); m(){ class Machin{ // attributs Bidule b; // methodes void m(){ Une interface est une abstraction de classe et ne déclare que les signatures des fonctions. class Truc extends Chose, extends Machin{ Truc(){ this.b?? this.m()?? L'appel de fonction d'une interface est donc impossible. 2
Interface (1/3) Une interface est une liste de méthodes abstraites (signature uniquement) et de constantes (le mot-clé final n'est pas nécessaire car implicite). Une interface ne peut être instanciée et n'a donc pas de constructeur. Une interface peut hériter d'une ou plusieurs autres interfaces. interface Fetard{ //attributs final float ALCOOLEMIE_MAX = 5.0f; // méthodes void participefete(fete f); void faitleplein(float litres); interface Conducteur{ //attributs final float ALCOOLEMIE_MAX = 0.5f; // méthodes void conduit(voiture v); void faitleplein(float litres); interface FetardConducteur extends Fetard, Conducteur{ Le polymorphisme d'attribut peut être géré à la compilation. 3
Interface (2/3) Une classe peut «hériter» d'autant d'interfaces qu'elle veut en utilisant le mot-clé implements. Une classe qui implémente une interface doit rédéfinir les méthodes abstraites, sinon la classe est abstraite. class EtudiantFetardConducteur extends Etudiant implements Fetard, Conducteur{ float participefete(fete f){ void conduit(voiture v){ void faitleplein(float litres){ this.alcoolemie_max?? 4
Interface (3/3) Une interface définit un type de données abstrait, mais qui peut être utiliser pour typer des variables. Fetard f = new EtudiantFetardConducteur(); f.faitleplein(2.0f); f.conduit(new DeuxChevaux()); f.alcoolemie_max = 3.0f; Conducteur[] liste = new Conducteur[3]; liste[0] = new Conducteur(); On peut donc définir un ensemble, hiérarchisé par héritage, de types abstraits (non instanciables) en plus des types concrets (instanciables). Ces types abstraits ne servent pas qu'à faire de l'héritage presque multiple, mais aussi à structurer davantage les programmes. 5
Classe abstraite (1/4) Une classe qui implémente une (ou plusieurs) interface(s) sans redéfinir toutes les méthodes abstraites est également abstraite et doit être déclarée telle avec le mot-clé abstract. Une classe abstraite ne peut être instanciée. Une classe dont la définition est préfixée par abstract est abstraite, même si elle n'implémente pas d'interface et n'a pas de méthode abstraite. abstract class CapitaineDeSoiree extends Etudiant implements Fetard, Conducteur{ float participefete(fete f){ void faitleplein(float litres){ 6
Classe abstraite (2/4) Une classe abstraite peut être utile lorsqu'on veut n'implémenter qu'une partie des méthodes d'une interface. abstract class CapitaineDeSoiree extends Etudiant implements Fetard, Conducteur{ float participefete(fete f){ void faitleplein(float litres){ class ChauffeurFiable extends CapitaineDeSoiree{ void conduit(voiture v){ v.bienconduire(); class ChauffeurNonFiable extends CapitaineDeSoiree{ void conduit(voiture v){ v.conduirenimportecomment(); 7
Classe abstraite (3/4) Une classe définit une méthode abstraite en donnant sa signature et en la préfixant par le mot-clé abstract. Toute classe qui définit une méthode abstraite est abstraite et doit être déclarée comme telle. abstract class CapitaineDeSoiree extends Etudiant implements Fetard, Conducteur{ abstract void drague(personne p); class CapitaineDeSoireeEnCouple extends CapitaineDeSoiree{ void drague(personne p){ class CapitaineDeSoireeCelibataire extends CapitaineDeSoiree{ void drague(personne p){ this.payeunverre(p); this.danseavec(p); 8
Classe abstraite (4/4) Une classe abstraite peut être utile pour imposer l'existence de certaines méthodes dans des classes, sans savoir comment elles seront implémentées. abstract class Animal{ abstract String cri(); class Chat extends Animal{ String cri(){ return "miaou"; class Chien extends Animal{ String cri(){ return "ouahouah"; Une classe qui hérite d'une classe abstraite doit redéfinir les méthodes abstraites, sinon elle est abstraite également. En C++, les méthodes abstraites sont dites virtuelles pures : virtual void methodeabstraite() = 0; 9
Classe abstraite ou interface? Une interface sert à définir un comportement : quand on veut définir uniquement la spécification du comportement d'une catégorie d'objets. quand on veut faire de l'héritage presque multiple. Une classe abstraite, comme toute classe, sert à factoriser du code : quand on veut définir le comportement d'une catégorie d'objets via des méthodes abstraites et des méthodes concrètes. Un programme n'est jamais trop abstrait, il ne faut pas hésiter à créer des interfaces et des classes abstraites qui structurent le code et facilitent sa maintenance, son extensibilité et sa réutilisation. 10
Abstraction et réutilisation Bidouille : class Animal{ String cri(){ if(this instanceof Chat) return "miaou"; if(this instanceof Chien) return "ouahouah"; if(this instanceof Canari) return "cuicui"; Programme de qualité : abstract class Animal{ abstract String cri(); class Chien extends Animal{ String cri(){ return "ouahouah"; class Chat extends Animal{ String cri(){ return "miaou"; class Canari extends Animal{ String cri(){ return "cuicui"; 11
Abstraction L'abstraction permet de décrire des comportements via des méthodes abstraites, en ne précisant que les entrées-sorties de ces méthodes. Ces méthodes abstraites peuvent être définies dans des interfaces (où toutes les méthodes sont abstraites) ou dans des classes abstraites (où au moins une méthode est abstraite) qui sont des types de données abstraits. class C1 abstract class C2 interface I1 interface I2 class C3 class C4 interface I3 class C5 12
Design Pattern Interfaces et classes abstraites sont combinées dans des patrons de programmation (design pattern) pour répondre aux problèmes pratiques de programmation objet. Exemple : le pattern strategy, permettant de changer dynamiquement le comportement des instances d'une classe au cours de l'exécution d'un programme. 13
Membres de classe Une classe est une description, à un niveau plus abstrait, des objets qui en sont les instances. Mais il peut être utile de décrire des données ou des comportements véritablement communs à toutes les instances : - attributs avec la même valeur pour toutes les instances - méthodes s'exécutant de la même façon pour toutes les instances class Quadrupède{ // attributs int nbpattes = 4; // méthodes void classerparpoids(quadrupède[] t){ 14
Attribut de classe (1/2) Un attribut de classe, préfixé par le mot-clé static, est lié à sa classe et non aux instances. Sa valeur est donc la même pour tous les objets. Un attribut de classe peut être accédé via la classe ou via une instance. class Humain{ // attributs String nom; static Humain ancetre = new Humain("Lucy"); public static void main(string[] pouet){ Humain h = new Humain("Toto"); Humain l = h.ancetre; l = Humain.ancetre; System.out.println(ancetre); Une méthode d'instance peut accéder/modifier un attribut de classe (la valeur change alors évidemment pour toutes les instances). 15
Attribut de classe (2/2) L'initialisation des attributs de classe peut se faire à la déclaration des attributs, ou dans un bloc static exécuté au chargement de la classe. class Humain{ // attributs String nom; static Humain ancetre = new Humain("Lucy"); static ArrayList<Droit> ddl; // initialisations static{ ddl = new ArrayList<Droits>(); ddl.add(new Droit("Liberté")); ddl.add(new Droit("Egalité en droit")); Un attribut de classe non modifiable (final) est une constante de classe. Math.PI; Math.E; 16
Méthode de classe (1/2) Une méthode de classe, préfixée par static, s'exécute de la même façon, à partir d'une classe ou d'une instance. class Humain{ // methodes static void ajoutdroit(droit d){ ddl.add(d); static void premierpassur(planete p){ System.out.println("un grand pas pour l'humanité"); public static void main(string[] pouet){ Humain h = new Humain("Toto"); h.premierpassur(lune); Humain.premierPasSur(mars); Humain.ajoutDroit(new Droit("Mourir dans la dignité"); 17
Méthode de classe (2/2) Une méthode de classe ne possède pas d'objet courant (pas de this) donc elle ne peut pas accéder aux membres d'instance. class Humain{ // attributs String nom; // methodes void nait(){ System.out.println("Ouuiiiinnnnn"); static void premierpassur(planete p){ System.out.println("un grand pas pour l'humanité"); System.out.println("je m'appelle "+nom); nait(); La méthode main est obligatoirement de classe car elle sert de point d'entrée des programmes. 18
Membres de classe et héritage (1/2) Les attributs de classe peuvent être redéfinis (masqués), les méthodes de classe peuvent être redéfinies ou surchargées. Les appels aux attributs et méthodes de classe sont résolus à la compilation (résolution statique). Donc les membres de classe appelés sur un objet sont ceux de la classe qui type la variable qui stocke l'objet. class Homme{ // attributs static Homme ancetre = new Homme("Adam"); // methodes static void premierpassur(planete p){ System.out.println("un grand pas pour l'homme"); public static void main(string[] s){ Humain h = new Homme("Toto"); System.out.println(h.ancetre.nom); h.premierpassur(jupiter); 19
Membres de classe et héritage (2/2) Il n'est pas possible de redéfinir une méthode de classe par une méthode d'instance ou inversement. class A{ // methodes static void m1(){ void m2(){ class B extends A{ // methodes void m1(){ static void m2(){ 20
Membres de classes et abstraction Une méthode de classe ne peut être abstraite car sa classe doit définir le corps de la méthode. class Humain{ // methodes static abstract void premierpassur(planete p); public static void main(string[] pouet){ Humain.premierPasSur(lune);? Les attributs constants des interfaces sont implicitement des attributs de classe (mais le mot-clé static n'est pas nécessaire). interface Enfant{ // attributs final static Personnage pere_noel = ; Personnage // methodes void jouer(jeu j); void faire_des_betises(); 21
Chargement des classes (1/2) En Java, les classes sont chargées dans la machine virtuelle lorsque c'est nécessaire uniquement, en cas de création d'instance ou lors d'appel de méthode de classe. Les classes du noyau de l'api Java sont chargées par défaut. class Humain{ // attributs String nom; static Humain ancetre = new Humain("Lucy"); public static void main(string[] s){ Humain h = new Humain("Toto"); System.out.println(h.ancetre.nom); Ce chargement paresseux (lazy loading) permet de modifier dynamiquement les programmes. Ce n'est pas le cas en C++. class Homme{ static{ Humain.ancetre = new Humain("Adam"); 22
Chargement des classes (2/2) La JVM, ou les programmes eux-mêmes, peuvent charger des classes au cours de l'exécution des programme par des instances de Classloader (classe abstraite). Un ClassLoader permet de récupérer un objet de type Class qui représente une classe. Dans la JVM, il peut exister au même moment plusieurs ClassLoader qui ont chargé les mêmes classes. Java Virtual Machine Espace de nommage de cl1 Espace de nommage de cl2 ClassLoader cl1 Class Humain Class Femme ClassLoader cl2 Class Homme Class Humain Class Homme Class Femme 23
La classe Class (1/2) Il existe plusieurs méthodes pour récupérer les instances de Class. ClassLoader cl = Homme.class.getClassLoader(); Class c = cl.loadclass("humain"); Class d = Class.forName("Femme"); Humain h = new Humain("Toto"); h.getclass(); Les tableaux et les types primitifs (boolean, int, etc) sont aussi des types représentés par des instances de Class via les classes correspondantes (Boolean, Integer, etc). Il est impossible de créer dynamiquement de nouvelles classes en appelant un constructeur de Class. Seul le chargement de classes préalablement définies est possible. 24
La classe Class (2/2) La classe Class permet d'accéder à des instances représentant les membres des classes. ClassLoader cl = Homme.class.getClassLoader(); Class c = cl.loadclass("humain"); Field[] attributs = c.getfields(); // attributs hérités ou non Method[] methodes = c.getmethods(); // méthodes héritées ou non Constructor[] constructs = c.getconstructors(); Class sc = c.getsuperclass(); Class[] inter = c.getinterfaces(); La classe Class permet de créer des instances si la classe est instantiable et possède un constructeur sans paramètre. Class c = cl.loadclass("humain"); Humain h = (Humain) c.newinstance(); 25
La classe Field La classe Field permet d'obtenir toutes les informations sur un attribut, et de modifier les valeurs d'attributs des instances de la classe correspondante. Field f = // f est un champ de la classe C String s1 = f.getname(); Class cl1 = f.gettype(); Class dc1 = f.getdeclaringclass(); boolean stat1 = Modifier.isStatic(f.getModifiers()); boolean fin1 = Modifier.isFinal(f.getModifiers()); Object o = f.get(obj); // o contient la valeur de f sur l'objet obj Object o = // o est une instance de C Object v = // v est une instance de cl1 f.set(o,v); 26
La classe Method La classe Method permet d'obtenir toutes les informations sur une méthode, et d'appeler cette méthode sur des valeurs. Method m = // m est une méthode de la classe C String s1 = m.getname(); Class cl1 = m.getreturntype(); Class[] param1 = m.getparametertypes(); Class dc1 = m.getdeclaringclass(); boolean stat1 = Modifier.isStatic(m.getModifiers()); boolean fin1 = Modifier.isFinal(m.getModifiers()); Object o = // o est une instance de C Object[] t = {v0, v1, // chaque vi est une instance de param[i] m.invoke(o,t); 27
Introspection La modularité et l'abstraction permet l'introspection des programmes objets. Un programme peut s'auto-analyser ou analyser des objets chargés au cours de l'exécution, même si leurs types ne sont pas connus lors de l'écriture du programme. L'introspection améliore donc la généralité des programmes. Les applications réparties peuvent interagir par introspection avec n'importe quel programme objet sans avoir à connaître à l'avance la structure de ce programme. Les IDE (Integrated Development Environment) utilisent également l'introspection pour l'aide à l'écriture et au test des programmes. 28
Introspection et encapsulation L'introspection pourrait poser des problèmes si les objets montraient tout de leur structure interne et que n'importe quel objet pouvait utiliser tous leurs attributs et méthodes. Les mécanismes d'introspection de Java ne permettent d'accéder qu'aux classes, attributs et méthodes déclarés visibles (public). class Espion{ // attributs private Mission m; public Personnage couverture; private void elimination(espion e){ 29