Adloun

programmation Java (école d'ingénieur) · Chapitre 8 · Exercice de TD

Une date toujours valide

Encapsulation, membres de classe et paquets · niveau 3 (difficile)

Énoncé

Écrire une classe immuable Date (jour, mois, année) dont l'invariant est « la date existe dans le calendrier grégorien ». Une année est bissextile si elle est divisible par 4 mais pas par 100, ou divisible par

  1. Écrire :
  • les méthodes static estBissextile(int a) et joursDansMois(int m, int a) ;
  • le constructeur, qui refuse toute date invalide ;
  • une méthode lendemain() ;
  • toString() au format jj/mm/aaaa.

Tester les lendemains du 28/02/2024, du 28/02/2026 et du 31/12/2026, et la validité du 29/02/1900, du 29/02/2000 et du 31/04/2026.

Corrigé

Le constructeur vérifie l'invariant avec joursDansMois ; lendemain distingue trois cas (même mois, mois suivant, année suivante), et chaque nouvelle date repasse par le constructeur.


// Une date toujours valide, immuable.
final class Date {
    private final int jour, mois, annee;

    Date(int jour, int mois, int annee) {
        if (mois < 1 || mois > 12 || jour < 1 || jour > joursDansMois(mois, annee)) {
            throw new IllegalArgumentException("date invalide : " + jour + "/" + mois + "/" + annee);
        }
        this.jour = jour;
        this.mois = mois;
        this.annee = annee;
    }

    static boolean estBissextile(int a) {
        return (a % 4 == 0 && a % 100 != 0) || a % 400 == 0;
    }

    static int joursDansMois(int m, int a) {
        switch (m) {
            case 2: return estBissextile(a) ? 29 : 28;
            case 4: case 6: case 9: case 11: return 30;
            default: return 31;
        }
    }

    Date lendemain() {
        if (jour < joursDansMois(mois, annee)) return new Date(jour + 1, mois, annee);
        if (mois < 12) return new Date(1, mois + 1, annee);
        return new Date(1, 1, annee + 1);
    }

    @Override
    public String toString() { return String.format("%02d/%02d/%d", jour, mois, annee); }
}

public class Ex11Date {
    public static void main(String[] args) {
        System.out.println(new Date(28, 2, 2024).lendemain());
        System.out.println(new Date(28, 2, 2026).lendemain());
        System.out.println(new Date(31, 12, 2026).lendemain());
        for (int[] d : new int[][] {{29, 2, 1900}, {29, 2, 2000}, {31, 4, 2026}}) {
            try {
                System.out.println(new Date(d[0], d[1], d[2]) + " valide");
            } catch (IllegalArgumentException e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

29/02/2024
01/03/2026
01/01/2027
date invalide : 29/2/1900
29/02/2000 valide
date invalide : 31/4/2026

1900 est divisible par 100 sans l'être par 400 : elle n'est pas bissextile. 2000, divisible par 400, l'est. Dans le constructeur, l'ordre des tests compte : on vérifie que le mois est dans avant d'appeler joursDansMois. Ici, l'appel avec un mois invalide retournerait 31 sans dommage, mais dans une version qui lirait un tableau indexé par le mois, il provoquerait un débordement d'indice. La bibliothèque de Java fournit une classe java.time.LocalDate qui fait tout cela, et bien plus.

100 % gratuit

La suite du chapitre est sur Adloun

Exercice suivant et tuteur

Sans compte : le cours, le premier QCM et 2 minutes de podcast et de vidéo. Exercices et tuteur : inscription gratuite en 30 secondes, sans carte bancaire.

Le cours du chapitre · Tous les exercices du chapitre

Exercice suivant et tuteur →