Poo 10 Heritage
Poo 10 Heritage
Poo 10 Heritage
Rémi Forax
java.lang.Object
java.lang.Object
Classe “mère” de toutes les autres classes
public class Author { }
…
Object o = new Author(); // ok
Ne marche pas avec les types primitifs (on doit utiliser les wrappers)
Object.getClass()
Renvoie un objet de type java.lang.Class qui
représente la classe d’un objet à l’exécution
– La notation Foo.class permet d’obtenir le même objet
Exemple
"hello".getClass() // String.class
String s = "hello";
s.getClass() // String.class
Object o = "hello";
o.getClass() // String.class
toString()/equals()/hashCode()
java.lang.Object fournit une implantation par défaut
– equals(Object o)
return this == o;
– hashCode()
renvoie un nombre tiré au hazard, 1 seul fois !
– String toString()
return getClass().getName() + "@" + hashCode()
Héritage Interface
sous-typage sous-typage
La grosse différence est que les interfaces ne gèrent pas les champs
Héritage et maintenance
Hériter pose de vrais problèmes
– Modularité
●
L’implantation n’est plus à un seul endroit, il faut regarder
toute la hiérarchie
– Gros problème si on hérite d’une classe du JDK/d’une librairie,
on ne contrôle plus l’implantation
– Correction du code
●
Il faut redéfinir toutes les méthodes qui n’ont pas la
bonne sémantique
– Gros problème si on hérite d’une classe du JDK/d’une librairie,
on doit redéfinir des méthodes qui n’existent pas encore
Héritage et maintenance (2)
Hériter des champs pose de vrais problèmes
– Mutabilité
●
La classe qui hérite peut avoir un champ non final,
donc aucun type n’est vraiment non mutable
– Egalité
●
Si on a un equals() dans la super-classe,
coloredPoint.equals(point) et point.equals(coloredPoint)
n’exécutent pas le même code
Interface et délégation
(comment ne pas faire d’héritage)
On utilise une interface
On utilise une interface pour le sous-typage
public interface Point {
double distanceToOrigin();
}
public class Point2D implements Point {
private final int x;
private final int y;
public double distanceToOrigin() { /* 1 */ }
}
public class ColoredPoint implements Point {
private final int x;
private final int y;
private final Color color;
public double distanceToOrigin() { /* 2 */ }
}
Mais cela veut dire que l’on va dupliquer du code ?
On utilise la délégation
On utilise la délégation pour réutiliser le code
public interface Point {
double distanceToOrigin();
}
public class Point2D implements Point {
private final int x;
private final int y;
public double distanceToOrigin() { /* 1 */ }
}
public class ColoredPoint implements Point {
private final Point2D point; délégation
private final Color color;
public double distanceToOrigin() { return point.distanceToOrigin(); }
}
Réutilisation par délégation
La délégation permet de choisir les méthodes que l’on
expose dans l’API
– Avec l’héritage (beurk !)
public class Library extends ArrayList<Book> {}
Il faut redéfinir toutes les méthodes qui permettent d’ajouter un livre
pour faire un test à null: add(), addAll(), ListIterator.add() + celles qui
n’existent pas encore.
– Avec la délégation
public class Library {
private final ArrayList<Book> books; // délégation
// ici, on est libre d’exposer uniquement les méthodes que l’on veut
// et de faire uniquement les tests à null nécessaires, on contrôle l’API
}
Empêcher l’héritage et mutation
Classe final
Si une classe est déclarée final, alors on ne
peut pas en hériter
– Toutes les classes non mutables doivent être final
sinon une sous-classe peut avoir un champ non final
– Tous les records sont final
Copier/coller du C++
pas utilisé en pratique sauf dans les vieux codes (< 2000)
Surcharge vs Redéfinition
Surcharge vs Redéfinition
(si on hérite ou implante une interface)
Mais la signature d’une méthode est stockées sous forme d’une String
dans le fichier .class, pour une recherche rapide, donc le type
des paramètres doit être exactement le même en Java
Type des exceptions checkées
public class Point {
Site d’appel visibilité retour method(paramètres)
throws exceptions {
Point p = new ColoredPoint(); ...
ion
}
t
ila
try {
mp
}
retour result =
Co
public class ColoredPoint extends Point {
p.method(paramètres); Execution visibilité2 retour2 method(paramètres2)
} catch(exceptions) { throws exceptions2 {
... ...
}
} }
Point vtable
0: toString(Object this) {
class equals(Object this, …) {
1:
x 3
2: hashCode(Object this) {
y 4
3: m(Point this) { 1 }
4: m2(Point this) {
class
x 3 ColoredPoint vtable
y 4
0:
color "red" 1:
2:
3: m(ColoredPoint this) { 2 }
4:
for(Point point: points) {
point.m() // point.vtable[3]
}
En résumé
En résumé
L’héritage est le goto de la POO
– Préférer les rateaux aux arbres (les interfaces à l’héritage)
– Si une librairie utilise l’héritage, comment on fait ?
●
On utilise l’héritage, on n’a pas le choix