Exercice 1 : La classe Student
Le but de l’exercice est de reproduire ce que nous avions fait au TP précédent avec les listes d’étudiants, mais cette fois en utilisant une structure de classe.
Création du package et de la classe
Créez un nouveau projet Java intitulé “BaseEtudiants”
Dans ce projet, créez les classes (avec les bons packages)
fr.upsaclay.bibs.students.Student fr.upsaclay.bibs.BaseEtudiantsLa classe
BaseEtudiantscontiendra la fonctionmainDans la classe
Student, créez deux champsfirstNameetlastNamepour stocker le prénom et le nom de l’étudiant. Les champs doivent être publics et finaux.Créez un constructeur qui prend en paramètre un prénom et un nom et initialise les champs de telle sorte à ce que
- les espaces de début et de fin soient supprimés (méthode
stripdes objets de typeString) - le nom de famille
lastNamesoit enregistré en majuscule
- les espaces de début et de fin soient supprimés (méthode
Créez une fonction
maindans la classeBaseEtudiantset créez un objet de type étudiant. Vérifiez que vous arrivez bien à exécuter
Premiers tests
Créez un package
fr.upsaclay.bibs.students.testDans ce package, créez une classe
StudentTestet ajoutez les import suivantimport static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; import fr.upsaclay.bibs.students.Student;Dans Intellij, cliquez sur le mot souligné en rouge dans l’import : corrigez l’erreur en ajoutant JUnit 5 à votre “classpath” (attention ! Il faut ajouter JUnit5 et non pas JUnit4 qui est parfois proposé par défaut)
Ajoutez la méthode
testCreationsuivante
@Test
void testCreation() {
assertEquals(new Student("Ada","Lovelace").firstName, "Ada");
}Puis lancez l’exécution de l’unité de test 8. Complétez votre méthode et votre classe avec le contenu présent ici. Décommentez les tests de la méthode testCreation et vérifiez que cela passe bien.
Equals, toString et hashCode
- On considère (sans doute à tort) que deux étudiants sont “égaux” s’ils ont les mêmes noms et prénoms. Ajoutez et complétez les 3 méthodes suivantes. La méhode
toStringdoit renvoyer le nom (en majuscule) suivi du prénom séparé par un espace.
@Override
public String toString() {
}
@Override
public boolean equals(Object obj) {
}
@Override
public int hashCode() {
}- Vérfier la méthode
toStringen faisant un affichage dans la fonctionmain - Décommentez les tests correspondants aux 3 méthodes et lancez les.
Test validName
- Implantez la méthode suivante dans la classe
Student. Puis décommentez les tests correspondants dansStudentTestet lancez-les.
/**
* Checks that a given String is a valid name
* ie it contains only: letters, spaces, or "-"
* @param name a String representing a name
* @return true if it's a valid name, false otherwise
*/
public static boolean validName(String name)Dans le constructeur de la classe
Student, rajoutez un test pour la validité du prénom et du nom. En cas de nom invalide, vous léverez une exception avec la ligne suivante :throw new IllegalArgumentException("Not a valid name");Décommentez et lancez les tests de la méthode
testExceptionCreationdansStudentTest
Nouveau champ : getter et setter
- Créez un nouveau champ de type
String:emailavec visibilitéprivate - Créez deux méthodes
public void setEmail(String email)etpublic String getEmail()qui permette de donner une valeur / récupérer la valeur du champemail.
Il est très courant d’utiliser la visibilité private pour des champs et d’en contrôler l’accès par des méthodes appelées “getter” et “setter”. D’ailleurs, une fois qu’on a créé un champ, on peut générer automatiquement ces méthodes avec intellij (bouton droit sur le nom de la classe puis “Generate”).
- Ajoutez un constructeur
public Student(String firstName, String lastName, String email)qui fait appel au constructeur principal pour le prénom / nom puis initialise l’email. - Décommentez et lancez les tests de la méthode
testEmaildansStudentTest - On peut tester la validité d’un email grâce à une “expression régulière” en utilisant la classe
java.util.regex.Pattern. Ajoutez la méthode suivante à votre classeStudent(il faut aussi importer la classePattern). (C’est une vérification très basique qui cherche simplement le caractère “@”)
/**
* Checks that a given String is a valid email using regular expression
* @param email a String representing an email
* @return true if it's a valid email, false otherwise
*/
public static boolean validEmail(String email) {
String regexPattern = "^(.+)@(\\S+)$";
return Pattern.compile(regexPattern)
.matcher(email)
.matches();
}Utilisez cette méthode pour vérifier la validité d’un email avant de mettre à jour le champ. Si l’email est invalid, on lèvera une exception :
throw new IllegalArgumentException("Not a valid email");
Décommentez et lancez les tests des méthodes testValidEmail et testExceptionEmail
Implémenter Comparable
Changez la déclaration de la classe
Studentpour qu’elle implémente l’interfaceComparable:public class Student implements Comparable<Student>Intellij affiche maintenant une erreur : il faut ajouter la méthode
compareTo(Student st): implémentez la méthode en utilisant la méthodecompareTodes chaînes de caractères.Décommentez et lancez les tests de la méthode
testCompareTo
Une variable statique
Comme nous avons codé l’égalité avec la comparaison des noms, il se peut que nous ayons des instances avec des informations différentes (l’email) mais considérées comme égales. On voudrait savoir laquelle est la plus récente. Pour cela, on va rajouter un champ instanceId : chaque instance créée reçoit un nouvel id. Plus l’id est grand, plus l’instance est récente.
Créer deux champs dans la classe
Studentprivate int instanceId; private static int count = 0;A chaque création, augmenter la variable statique
countet utiliser la valeur courante comme id du nouvel objet. Le champcountest une variable statique qui compte le nombre d’instances crées.Ajoutez une méthode
getInstanceIdpuis décommentez et lancez les tests de la méthodetestInstanceId.
Exercice 2 : la classe Grade
- Dans le package
fr.upsaclay.bibs.students, créez une nouvelle interfaceGradableainsi qu’une classeGradeet copiez les codes de l’interface ici et de la classe ici - Dans le package
fr.upsaclay.bibs.students.testcréez une nouvelle classeGradeTestet copiez le code présent ici - La classe
Gradereprésente une note que l’on peut calculer sur la valeur souhaitée et qui peut aussi stocker un coefficient
- la représentation en chaine écrit la note sur la valeur maximale par défaut “10/20”
- le test d’égalité s’effectue sur la valeur de la note tel que 10/20 soit égal à 50/100
- le hashcode doit être cohérent par rapport à ça (on ne peut donc pas utiliser la représentation sous forme de chaîne de caractère !)
- la classe implémente l’interface
Gradablequi contient en particulier une méthode pour renvoyer la note avec la note maximale que l’on souhaite (note /20, /10, /100, etc) - la classe
Gradeimplémente deux interfaces, l’interfaceGradableet l’interfaceComparable: on vous fournit la méthodecompareToqui utilise une méthode statique définie dans l’interface.
Complétez toutes les méthodes de la classe Grade de telle sorte que les tests définis dans GradeTest passent tous.
Code dans les interfaces
Au départ, le rôle d’une interface n’est pas d’implémenter du code (ça c’est le rôle des classes). Deux exceptions cependant :
- les méthdoes statiques offrent des fonctions utilitaires sur les objets implantant l’interface
- des méthodes
default: cela sert à définir des méthodes plus ou moins “optionnelles”. On veut que toutes les notes aient un coefficient mais ça ne sera pas forcément pertinents pour toutes les classesGradabledonc on fournit un comportement par défaut. Cela a été introduit dans la version 8 de Java et est très utile pour la “Backward compatibility” (compatibilité des applications avec les versions antérieures) : on peut étendre une interface sans devoir réimplanter toutes les classes qui l’implémentent.
Exercice 3 : La classe GradeList
- Dans le package
fr.upsaclay.bibs.etudiants, créez une nouvelle classeGradeListet copiez le code présent ici. Dans le packagefr.upsaclay.bibs.etudiants.test, créez une classeGradeListTestet copiez le code présent ici et vérifiez que les tests passent
Classes génériques
Cette classe sert à stocker une liste de notes et est elle-même une implantation de Gradable et de Iterable. Surtout, c’est elle-même une classe générique : elle stocke une liste d’éléments de type “E” où E doit être une implantation de Gradable. Cela vous donne un exemple d’implantation générique mais on ne vous demande pas de maîtriser ce système à ce stade.
La plupart des méthodes de
GradeListsont déjà implantées (regardez par exemple les constructeurs et le champ privé pour stocker la liste). Il ne reste que la méthodegradeOverà implanter : cette méthode renvoie la moyenne des notes stockées. Ne vous laissez pas impressionner par le type génériqueE, vous avez un exemple juste au dessus de parcours de la liste avec la méthodemax. Implantez la méthodegradeOverde telle sorte que les tests à décommenter passent.Complétez les tests de la méthode
testSetGetCoefficientdansGradeListTestOn va maintenant éditer la classe
Studentpour qu’on puisse stocker pour chaque étudiant une liste de note. Ajoutez un champ privé final dans la classeStudent:private final GradeList<Gradable> gradeList = new GradeList<Gradable>();Ajoutez une méthode dans la classe
Studentpour récupérer la liste de note.public GradeList<Gradable> getGradeList() { return gradeList; }Vérifiez que les tests de
testGetGradeListfonctionnent.Modifiez votre déclaration de classe
Studentpour qu’elle implémente l’interfaceGradableen plus deComparable(on écrit les différentes interfaces implémentées en séparant par des virgules). La seule méthode à implanter estgradeOverqui doit renvoyer la note calculée pargradeList(c’est-à-dire la moyenne de l’étudiant). Vérifiez que les tests detestDefaultCoefficientettestGradeOverfonctionnent.
Polymorphisme
La classe Student est maintenant une implantation de Gradable : cela signifie que tout ce qui peut s’appliquer aux objets Gradable peut s’appliquer aux étudiants. En particulier, on peut créer une GradeList avec une liste d’étudiants ! C’est ce qu’on va faire juste après pour calculer la moyenne d’un groupe d’étudiants.
En fonction du type déclaré Student ou Gradable on aura accès à plus ou moins de fonctionnalités : c’est le polymorphisme.
Exercice 4 : une fonction main
Récupérez le nouveau fichier
grades.txtprésent ici. Chaque ligne suit le format suivantNOM, Prénom, note, note, note
On considère que la première note a coefficient 2 tandis que les 2 autres notes ont chacune coefficient 1.
Écrivez une fonction main qui crée à partir du fichier une liste d’étudiants de type GradeList<Student> : chaque étudiant contiendra à son tour la liste de notes qui lui ait associée avec les bons coefficients.
Affichez :
- la liste des étudiants avec leur moyenne
- la moyenne générale de la classe (la moyenne des moyennes des étudiants) (Vous devez trouver 15.079)
- le nom et la note de l’étudiant-e ayant eu la meilleure moyenne (Vous devez trouver TSOGBEKAN Emeline avec la note 19.5)