Dans le chapitre précédent, nous avons vu la spécialisation. Dans celui-ci, nous allons aborder la généralisation.
Entre Product
, Television
et Fridge
, cela fait beaucoup de types différents. Nous allons voir ici comment travailler avec tous ces types.
Généralisation
L'ArrayList
contenue dans Bill
contient des Product
.
Va-t-il falloir ajouter deux autres ArrayList
pour contenir nos Television
et nos Fridge
?
Eh bien non ! Un objet Television
ou Fridge
est avant tout un objet de type Product
.
Nous pouvons donc sans problème faire :
public static void main(String[] args) {
Product cafe = new Product("Philips HD7866/61", "Philips SENSEO Quadrante, Noir - 1 ou 2 tasses", 79.99);
Television tv = new Television("TV Samsung UE49MU6292", "Smart TV LED incurvée 49\"", 599, 49, "LED");
Fridge fridge = new Fridge("BEKO TSE 1042 F", "Réfrigérateur BEKO 130L - Classe A+ - blanc", 189, 130, false);
Customer customer = new Customer("Juste Leblanc", "19 rue Germain Pilon, Paris");
Bill bill = new Bill(customer);
bill.addProduct(cafe, 1);
bill.addProduct(tv, 1);
bill.addProduct(fridge, 1);
}
Un objet Television
peut ainsi être considéré comme un objet Television
, mais aussi comme un objet Product
. Cette action est appelée une généralisation.
Product tv = new Television("TV Samsung UE49MU6292", "Smart TV LED incurvée 49\"", 599, 49, "LED");
Vous comprenez mieux pourquoi une ArrayList
ou une Map
peuvent être déclarées comme ceci :
private Map<Product, Integer> products = new HashMap<Product, Integer>();
HashMap
est un enfant de Map
, on peut l’utiliser comme une Map
.
Mais alors, pourquoi utiliser la généralisation ici ?
Il existe de nombreux types de Map
différents : HashMap
, ConcurrentHashMap
, LinkedHashMap
, etc.
Chacun a ses avantages et ses inconvénients, au niveau des performances. En fonction de la situation, il faut utiliser un type ou l’autre. Si, finalement, HashMap
n’est pas le type le plus adapté pour products
, on peut facilement en changer.
Surcharge
Pour l’instant, dans la spécialisation, nous avons vu l’ajout de nouveaux comportements.
Nous avons aussi la possibilité de surcharger un comportement. Nous l’avons déjà fait avec les constructeurs. Un objet Vehicle
nécessite des paramètres précis pour être construit. En créant Car
, nous avons ajouté des paramètres nécessaires au constructeur.
Donc Car
est un Vehicle
, mais ne se crée pas de la même façon.
Vous pouvez faire exactement la même chose avec une méthode.
On va changer le comportement de start
et de stop
dans Car
, pour afficher :
System.out.println("Je suis " + modelName + " je consomme " + literPer100km + " au 100km et je démarre !" );
System.out.println("Je suis " + modelName + " j'arrête mon moteur " + motor);
On va utiliser le mot clé @Override
pour faire cela :
public class Car extends Vehicle {
...
@Override
public void start() {
System.out.println("Je suis " + modelName + " je consomme " + literPer100km + "l au 100km et je démarre !" );
}
@Override
public void stop() {
System.out.println("Je suis " + modelName + " j'arrête mon moteur " + motor);
}
}
Ce code ne va pas fonctionner directement ! modelName
n’est pas accessible dans Car
. Il est private
et donc accessible uniquement dans Vehicle
. Il existe une visibilité permettant à la classe et ses enfants d'y avoir accès : protected
.
public class Vehicle {
protected String modelName;
...
}
Classe abstraite
À cette notion de surcharge est associée la notion de classe abstraite.
Finalement, nous pourrions nous dire qu'un véhicule n'existe pas forcément : il peut y avoir une voiture, un camion, un avion, mais pas un véhicule. Un véhicule est un concept abstrait, qui n'est pas suffisamment précis.
Ainsi, il faut garder ce concept de Vehicle
dans notre application, mais aucun développeur ne doit pouvoir en instancier un. Il devra instancier Car, Boat, Truck, etc. Dans cette même logique, le code à l'intérieur des méthodes start
et stop
n'a plus de sens dans Vehicle
. Il sera forcément @Override
dans les classes enfants.
Voilà comment représenter tout ça :
public abstract class Vehicle {
protected String modelName;
private String description;
private String manufacturer;
private int yeah;
private String color;
private int speed;
private int[] dimensions = new int[3];
private int weight;
public Vehicle(String modelName, String description, String manufacturer, int yeah, String color, int speed, int[] dimensions, int weight) {
this.modelName = modelName;
this.description = description;
this.manufacturer = manufacturer;
this.yeah = yeah;
this.color = color;
this.speed = speed;
this.dimensions = dimensions;
this.weight = weight;
}
public abstract void start();
public abstract void stop();
...
}
De cette façon, le code suivant ne peut plus fonctionner. Il y aura une erreur de compilation.
Vehicle vehicle = new Vehicle("Renault Super 5", ...
Toutes les classes qui héritent de Vehicle
devront forcément surcharger start
et stop
.

Gardez ces concepts en mémoire, nous allons les utiliser dès le prochain chapitre !