Abstraction - Introspection

Transcription

Abstraction - Introspection
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.
Une
interface
est
une
abstraction de classe et ne
déclare que les signatures des
fonctions.
class Chose{
// attributs
interface
Bidule Chose{
b;
////methodes
methodes
void
voidm();
m(){...}
}}
class Machin{
// attributs
Bidule b;
// methodes
void m(){...}
}
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 :
Programme de qualité :
class Animal{
...
String cri(){
if(this instanceof Chat) return "miaou";
if(this instanceof Chien) return "ouahouah";
if(this instanceof Canari) return "cuicui";
}
}
abstract class Animal{
...
abstract String cri();
}
class Chat extends Animal{
...
String cri(){
return "miaou";
}
}
class Chien extends Animal{
...
String cri(){
return "ouahouah";
}
}
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
...
class C3
...
class C4
...
interface I1
...
interface I2
...
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.
Les attributs constants des
interfaces sont implicitement
des attributs de classe (mais le
mot-clé static n'est pas
nécessaire).
class Humain{
...
// methodes
static abstract void premierPasSur(Planete p);
public static void main(String[] pouet){
Humain.premierPasSur(lune); ?
}
}
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
ClassLoader
cl1
Class Humain
Espace de nommage de cl2
Class Femme
Class Homme
Class Humain
ClassLoader
cl2
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
l'introspection pour l'aide à l'écriture et au test des programmes.
également
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

Documents pareils