Devoir surveillé - Conception par objet et programmation Java
Partie I - Analyse des instanciations et du polymorphisme Dans cette partie, nous analysons la validité de diverses instructions d'affectation et de transtypage (cast) en Java, en fonction des classes A , B , C et D liées par héritage ( D hérite de C , qui hérite de B , qui hérite de A ).
D'après le document Devoir surveillé - Conception par objet et programmation Java
Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Document source
Programming, Java · PDF · 8 pages · 2016
Afficher l'aperçu du document
Partie I - Analyse des instanciations et du polymorphisme
Dans cette partie, nous analysons la validité de diverses instructions d'affectation et de transtypage (cast) en Java, en fonction des classes A, B, C et D liées par héritage (D hérite de C, qui hérite de B, qui hérite de A).
Les instances initialisées sont :
A a = new B() ;(Type déclaré :A, Type réel :B)B b = new B() ;(Type déclaré :B, Type réel :B)C c = new D() ;(Type déclaré :C, Type réel :D)D d = new D() ;(Type déclaré :D, Type réel :D)
Instruction 1 : a = b ;
Cette instruction s'exécute normalement.
- Justification : La variable
best de typeBet la variableaest de typeA. Puisque la classeBest une classe fille de la classeA, l'affectation se fait via un transtypage implicite (upcast) qui est toujours valide à la compilation et à l'exécution.
Instruction 2 : b = (B) a ;
Cette instruction s'exécute normalement.
- Justification :
- À la compilation :
aest déclaré comme étant de typeA. Pour l'affecter àb(de typeB), un transtypage explicite(B)est requis (downcast). Le compilateur l'accepte carBhérite deA. - À l'exécution : L'instance réellement pointée par
aa été créée avecnew B(). Le transtypage versBcorrespond exactement à son type réel. L'opération est donc valide.
- À la compilation :
Instruction 3 : b = (D) c ;
Cette instruction s'exécute normalement.
- Justification :
- À la compilation :
cest déclaré comme typeC. Le transtypage explicite(D)vers une classe fille passe la compilation. L'expression devient de typeD. L'affecter àb(typeB) est un upcast implicite (carDhérite indirectement deB), ce qui est valide. - À l'exécution : L'instance pointée par
cest réellement de typeD(new D()). Le cast(D) cfonctionne donc parfaitement sans lever d'exception.
- À la compilation :
Instruction 4 : c = (C) b ;
Cette instruction déclenche une exception au niveau de l'exécution (ClassCastException).
- Justification :
- À la compilation :
best de typeB. Le transtypage explicite(C)(downcast vers une classe fille) est syntaxiquement correct pour le compilateur. - À l'exécution : L'instance pointée par
ba été créée avecnew B(). Une instance mère de typeBne possède pas les caractéristiques d'une classe filleCet ne peut jamais être transformée enC. La machine virtuelle Java rejettera cette opération.
- À la compilation :
Instruction 5 : b = (C) (D) d ;
Cette instruction s'exécute normalement.
- Justification :
- À la compilation :
dest de typeD. Le transtypage(D)est redondant mais valide. Ensuite, le transtypage(C)représente un upcast, ce qui est tout à fait valide. L'affectation de cette expression (considérée comme typeC) à la variableb(typeB) est également un upcast implicite (carChérite deB). - À l'exécution : L'instance est de type
D. Toutes les conversions faites ici remontent la chaîne d'héritage vers les classes mères (CpuisB). C'est une opération sécurisée.
- À la compilation :
Partie II - Application pour les stations de lavage
L'énoncé impose de respecter strictement la création de certaines méthodes (uniquement certains getters/setters). Pour que les classes enfants et la classe Station puissent accéder aux attributs non exposés par des getters (comme l'identifiant ou le type de carburant), nous déclarons les attributs en visibilité protected.
Pour que le code soit intégralement testable et compilable, une classe utilitaire Camion (mentionnée dans l'énoncé comme étant déjà implémentée) est fournie à titre indicatif.
Question 1 - Création de la classe Vehicule
Nous respectons l'énoncé en créant un constructeur complet, les getters et setters exclusifs à dateDeLavage et marque, ainsi que la redéfinition de toString et equals (basée sur l'identifiant et la date de lavage).
import java.util.Date;
public class Vehicule {
protected int id;
protected String marque;
protected Date dateDeLavage;
protected String typeDeCarburant;
// Constructeur paramétré
public Vehicule(int id, String marque, Date dateDeLavage, String typeDeCarburant) {
this.id = id;
this.marque = marque;
this.dateDeLavage = dateDeLavage;
this.typeDeCarburant = typeDeCarburant;
}
// Getters et Setters demandés
public String getMarque() {
return marque;
}
public void setMarque(String marque) {
this.marque = marque;
}
public Date getDateDeLavage() {
return dateDeLavage;
}
public void setDateDeLavage(Date dateDeLavage) {
this.dateDeLavage = dateDeLavage;
}
// Méthode toString
@Override
public String toString() {
return "Vehicule [ID=" + id + ", Marque=" + marque +
", Date=" + dateDeLavage + ", Carburant=" + typeDeCarburant + "]";
}
// Méthode equals (identiques si même ID et même date de lavage)
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Vehicule autre = (Vehicule) obj;
if (this.dateDeLavage != null) {
return (this.id == autre.id) && this.dateDeLavage.equals(autre.dateDeLavage);
} else {
return (this.id == autre.id) && (autre.dateDeLavage == null);
}
}
}
Question 2 - Création de la classe Voiture
La classe Voiture hérite de Vehicule. L'énoncé précise de ne fournir que le constructeur d'initialisation totale et la méthode toString (les getters/setters étant considérés comme acquis).
import java.util.Date;
public class Voiture extends Vehicule {
protected String couleur;
// Constructeur paramétré
public Voiture(int id, String marque, Date dateDeLavage, String typeDeCarburant, String couleur) {
super(id, marque, dateDeLavage, typeDeCarburant);
this.couleur = couleur;
}
// Getters et setters considérés comme implémentés par l'énoncé
public String getCouleur() {
return couleur;
}
public void setCouleur(String couleur) {
this.couleur = couleur;
}
// Méthode toString
@Override
public String toString() {
return "Voiture [Couleur=" + couleur + "] " + super.toString();
}
}
Note : Voici la classe minimale Camion (considérée comme déjà implémentée selon l'énoncé) nécessaire pour garantir que l'ensemble du projet compile.
import java.util.Date;
public class Camion extends Vehicule {
protected int nbrPlace;
public Camion(int id, String marque, Date dateDeLavage, String typeDeCarburant, int nbrPlace) {
super(id, marque, dateDeLavage, typeDeCarburant);
this.nbrPlace = nbrPlace;
}
@Override
public String toString() {
return "Camion [Places=" + nbrPlace + "] " + super.toString();
}
}
Question 3 - Création de la classe Station
La classe gère un tableau de véhicules limité à 10 places. L'utilisation d'un attribut nbVehiculesActuels est recommandée pour suivre le nombre exact de véhicules traités sans itérer sur des cases null.
Questions 3.a à 3.g : L'implémentation complète de Station
public class Station {
private int id;
private String nom;
private Vehicule[] vehicules;
private int nbVehiculesActuels; // Compteur pour gérer le tableau
// --- 3.a : Constructeurs ---
// Constructeur par défaut
public Station() {
this.vehicules = new Vehicule[10];
this.nbVehiculesActuels = 0;
}
// Constructeur paramétré (initialise la station avec id et nom, prépare le tableau)
public Station(int id, String nom) {
this.id = id;
this.nom = nom;
this.vehicules = new Vehicule[10];
this.nbVehiculesActuels = 0;
}
// Variante paramétrée si l'on souhaite passer un tableau existant (respect strict du "tous les attributs")
public Station(int id, String nom, Vehicule[] vehicules) {
this.id = id;
this.nom = nom;
this.vehicules = new Vehicule[10];
this.nbVehiculesActuels = 0;
if (vehicules != null) {
for (int i = 0; i < vehicules.length && this.nbVehiculesActuels < 10; i++) {
if (vehicules[i] != null) {
this.vehicules[this.nbVehiculesActuels] = vehicules[i];
this.nbVehiculesActuels++;
}
}
}
}
// --- 3.b : ajouterVehicule ---
public void ajouterVehicule(Vehicule v) {
if (v == null) return;
// Vérification de la limite de la station
if (nbVehiculesActuels >= 10) {
System.out.println("La station est pleine.");
return;
}
// Vérification des doublons via la méthode equals redéfinie
for (int i = 0; i < nbVehiculesActuels; i++) {
if (this.vehicules[i].equals(v)) {
System.out.println("Le véhicule est déjà en cours de traitement.");
return;
}
}
// Ajout
this.vehicules[nbVehiculesActuels] = v;
nbVehiculesActuels++;
}
// --- 3.c : supprimerVehicule ---
public void supprimerVehicule(Vehicule v) {
if (v == null) return;
for (int i = 0; i < nbVehiculesActuels; i++) {
if (this.vehicules[i].equals(v)) {
// Décalage des éléments vers la gauche pour garder le tableau compact
for (int j = i; j < nbVehiculesActuels - 1; j++) {
this.vehicules[j] = this.vehicules[j + 1];
}
// Nettoyage de la dernière case
this.vehicules[nbVehiculesActuels - 1] = null;
nbVehiculesActuels--;
return; // Suppression terminée
}
}
}
// --- 3.d : toString ---
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Station #").append(id).append(" - ").append(nom).append("\n");
sb.append("Véhicules en cours de traitement (").append(nbVehiculesActuels).append("/10) :\n");
if (nbVehiculesActuels == 0) {
sb.append(" Aucun véhicule.");
} else {
for (int i = 0; i < nbVehiculesActuels; i++) {
sb.append(" - ").append(vehicules[i].toString()).append("\n");
}
}
return sb.toString();
}
// --- 3.e : NbrVoitureEssence ---
public int NbrVoitureEssence() {
int compteur = 0;
for (int i = 0; i < nbVehiculesActuels; i++) {
// instanceof permet de filtrer uniquement les objets Voiture
if (vehicules[i] instanceof Voiture) {
// typeDeCarburant est accessible car défini en visibilité protected
if ("essence".equalsIgnoreCase(vehicules[i].typeDeCarburant)) {
compteur++;
}
}
}
return compteur;
}
// --- 3.f : NbrVoitureNoire ---
public int NbrVoitureNoire() {
int compteur = 0;
for (int i = 0; i < nbVehiculesActuels; i++) {
if (vehicules[i] instanceof Voiture) {
Voiture voitureCourante = (Voiture) vehicules[i];
if ("noire".equalsIgnoreCase(voitureCourante.getCouleur()) ||
"noir".equalsIgnoreCase(voitureCourante.getCouleur())) {
compteur++;
}
}
}
return compteur;
}
// --- 3.g : AfficherCamion ---
public void AfficherCamion() {
boolean trouve = false;
for (int i = 0; i < nbVehiculesActuels; i++) {
if (vehicules[i] instanceof Camion) {
System.out.println(vehicules[i].toString());
trouve = true;
}
}
// L'énoncé demande de lever très spécifiquement cette exception s'il n'y a rien à afficher
if (!trouve) {
throw new NullPointerException("Aucun camion en cours de traitement à afficher.");
}
}
}
Méthode
Face à une épreuve de conception orientée objet en Java :
- Polymorphisme et Transtypage (Casting) : Rappelez-vous la règle d'or : le compilateur valide le type de la variable (la référence de gauche), tandis que la machine virtuelle (JVM) valide le type réel de l'objet instancié en mémoire (le
newde droite). Un cast descendant (downcast, de mère vers fille) nécessite toujours une vérification explicite avec des parenthèses et porte le risque d'uneClassCastExceptionà l'exécution si le type réel n'est pas le bon. - Visibilité des attributs : Lorsque l'énoncé vous limite dans la création de Getters (comme à la question II.1), définissez toujours les attributs de la classe mère en
protected. Cela permet l'héritage direct et l'utilisation de ces données dans des classes associées s'ils se trouvent dans le même paquetage, sans bloquer votre propre code. - Gestion des tableaux classiques : Puisque les collections de type
ArrayListne sont pas demandées, gérez rigoureusement votre tableau primitif avec un entier de suivi (nbVehiculesActuels). Le retrait d'un élément (question 3.c) nécessite généralement de décaler les éléments suivants vers la gauche d'une case (comme un effacement de domino) pour éviter de retrouver des erreurs de pointeur nul (NullPointerException) lors de la navigation dans le tableau plus tard. - Opérateur
instanceof: Dans une liste hétérogène (polymorphique) regroupant tous les objets enfants sous un tableau du type de la classe mère, l'utilisation deinstanceofest la méthode standard pour compter ou extraire les données d'un seul type d'enfant (ex: isoler les Voitures des Camions). Toujours vérifier le type avant de forcer la conversion(Voiture).
Commentaires
Aucun commentaire pour le moment. Posez la première question.