:: Enseignements :: Licence :: L3 :: 2021-2022 :: Programmation Objet avec Java ::
[LOGO]

Examen de POO - session 1 2022


Tout document papier est proscrit. Vous avez le droit de lire le sujet jusqu'au bout, cela vous donnera une bonne idée de là où on veut aller !

Exercice 1 - Interfaces Fonctionnelles (4)

  1. Quelle est le nom de l'interface fonctionnelle qui prend un objet et renvoie un booléen ?
    Écrire un exemple de code créant une lambda qui prend paramètre un String et renvoie si la chaine de caractère commence par "foo".
    Note : il existe une méthode startsWith(prefix) sur String.
  2. Quelle est le nom de l'interface fonctionnelle qui prend un entier et renvoie void ?
    Écrire un exemple de code créant une lambda qui prend paramètre un entier et affichant celui-ci sur la sortie standard.
    Écrire le même code mais en utilisant une méthode référence à la place de la lambda.
  3. Quelle est le nom de l'interface fonctionnelle qui prend un entier long en paramètre et renvoie un entier long en retour ?
    Écrire un exemple de code créant une lambda qui prend paramètre un entier long et renvoie la valeur de cet entier long multiplié par 2.
  4. Quelle est l'interface fonctionnelle qui ne prend rien en entrée et renvoie void ?
    Écrire un exemple qui affiche sur la sortie d'erreur standard, "hello lambda".

Exercice 2 - Surcharge ou redéfinition (4)

  1. Le code suivant compile-t-il ? Si non pourquoi ? Si oui, y a-t-il redéfinition ou surcharge entre les méthodes m ?
         interface I {
           void m(I i);
         }
         class A implements I {
           public void m(A a) { }
         }
        
  2. Le code suivant compile-t-il ? Si non pourquoi ? Si oui, y a-t-il redéfinition ou surcharge entre les méthodes m ?
         class A {
           void m(A a) { }
         }
         class B extends A {
           void m(B b) { }
         }
        
  3. Le code suivant compile-t-il ? Si non pourquoi ? Si oui, y a-t-il redéfinition ou surcharge entre les méthodes m ?
         interface I {
           default void m() throws IOException { }
         }
         class A implements I {
           public void m() { }
         }
        
  4. Le code suivant compile-t-il ? Si non pourquoi ? Si oui, y a-t-il redéfinition ou surcharge entre les méthodes m ?
         interface I {
           protected void m(I i);
         }
         class A implements I {
           public void m(I i) { }
         }
        

Exercice 3 - Bank (12)

On souhaite modéliser une banque, ses comptes clients, les valeurs de la balance (la somme d'argent positive ou négative) de chaque compte, des ordres qui créditent ou débitent la balance d'un compte et enfin une notion d'audit qui permet de voir tous les ordres effectués par la banque (les crédits et les débits).
Dans notre design, la balance d'un compte est associée à un compte dans une structure de données annexe et n'est pas stockée dans le compte. Dans la vraie vie, cela permet d'avoir des niveaux de sécurité différents si on accède aux données d'un compte ou à la balance d'un compte.

  1. Dans un premier temps, on souhaite pouvoir créer des comptes (Account) et les ajouter à une banque. Un compte est défini par un numéro de compte (accountId) qui peut être positif, négatif ou nul, un nom de client (name), une date de naissance (dateOfBirth) entre 0 et 3000 et une ville de naissance (cityOfBirth).
    Voici une exemple de code qui doit fonctionner.
         var bank = new Bank();
         var account101 = new Account(101, "Joe First", 1999, "Paris");
         var account102 = new Account(102, "Jane Second", 2001, "Paris");
         var account007 = new Account(7, "James Bond", 1960, "London");
         bank.addAccount(account101);
         bank.addAccount(account102);
         bank.addAccount(account007);
        

    Pourquoi va-t-on utiliser un record pour représenter un Account ? Et pourquoi va-t-on utiliser une classe pour représenter une Bank ?
    Indiquer le code de Account en pensant à vérifier les pré-conditions.
    Indiquer le code de la classe Bank.

  2. On souhaite maintenant afficher tous les comptes d'une banque avec le code suivant
         System.out.println(bank);
         // 101: Joe First (1999, Paris)
         // 102: Jane Second (2001, Paris)
         // 7: James Bond (1960, London)
        
    Le format d'affichage pour un compte est le numéro du compte suivi d'un ': ', puis le nom du client puis la date de naissance et la ville de naissance entre parenthèses. Chaque compte doit être affiché sur sa propre ligne, il n'y a pas de ligne vide à la fin.
    Indiquer le code permettant d'avoir cet affichage sachant que l'implantation doit utiliser un stream.

  3. On veut ajouter une méthode findStartsWith renvoyant une liste non modifiable des comptes dont le nom du client commence par un préfixe indiqué en paramètre.
        var accountList = bank.findStartsWith("Ja");
         System.out.println(accountList);
         // [7: James Bond (1960, London), 102: Jane Second (2001, Paris)]
        

    La liste renvoyée doit être triée par l'ordre des noms des clients (ici, "James ..." est avant "Jane ...").
    Écrire une version de la méthode findStartsWith avec une boucle et utiliser List.sort(comparator) pour trier la liste.
    Note : il existe une méthode startsWith(prefix) sur String.
    Attention : la liste renvoyée doit être non modifiable !

  4. Écrire une nouvelle version de findStartsWith en utilisant un stream.
    Note : il existe une méthode sorted(comparator) sur un Stream.

  5. On souhaite maintenant ajouter une balance associée à un compte client. Pour cela, nous allons utiliser une table de hachage qui associe à un numéro de compte, la balance (un entier) de ce compte.
    Cela va aussi permettre de lever une exception si on essaie d'ajouter un compte qui possède le même numéro de compte qu'un compte précédemment ajouté.
    Indiquer le code du champ que l'on doit ajouter à la classe Bank.
    Puis indiquer le nouveau code de addAccount qui vérifie que l'on n'ajoute pas deux comptes ayant le même numéro et qui met la balance d'un compte créé à zéro.

  6. On souhaite pouvoir créer deux sortes d'ordres.
    • Un CreditOrder indique que la balance d'un compte client doit être augmenté d'un certain montant positif ou nul. À la création, le premier paramètre de CreditOrder est le montant (amount) et le second est le numéro de compte (accountId).
    • Un DebitATMOrder indique que la balance d'un compte client doit être diminuée d'un certain montant positif ou nul. A la création, le premier paramètre de DebitATMOrder est le montant (amount), le second est le numéro de compte (accountId) et le troisième est le numéro de la transaction VISA (visaId).
    Voici un exemple de code créant deux ordres
         var order1 = new CreditOrder(1_000, 101);
         var order2 = new DebitATMOrder(500, 7, 567_583);
        

    On souhaite qu'il ne soit pas possible de créer d'autres sortes d'ordre qu'un CreditOrder et un DebitATMOrder.
    Indiquer le code de CreditOrder et de DebitATMOrder sachant que le code qui vérifie que le montant d'un ordre est positif doit être partagé.
    Note : faites simple !

  7. On souhaite ajouter deux méthodes à une banque. Une méthode changeBalance qui prend en paramètre un numéro de compte et un montant (positif, négatif ou nul) et ajoute celui-ci à la balance du compte et une méthode balance qui renvoie la valeur de la balance d'un compte (ou zéro pour un compte inexistant).
         bank.changeBalance(101, 1_300);
         bank.changeBalance(7, 30);
         bank.changeBalance(101, 700);
         System.out.println(bank.balance(7));    // 30
         System.out.println(bank.balance(101));  // 2000
         System.out.println(bank.balance(102));  // 0
        
    Fournir le code des méthodes changeBalance et balance.

  8. On souhaite modifier l'affichage des comptes pour afficher aussi la valeur de la balance de chaque compte.
        System.out.println(bank);
        // 101: Joe First (1999, Paris) balance: 2000
        // 102: Jane Second (2001, Paris) balance: 0
        // 7: James Bond (1960, London) balance: 30
        
    Rappeler quel est le problème si une méthode publique d'une classe appelle une autre méthode publique de cette même classe. Comment résoudre ce problème ?
    Puis indiquer ce qui doit être changé et le nouveau code.

  9. On souhaite ajouter une méthode processOrder à la classe Bank qui effectue l'opération indiquée par l'ordre (créditer ou débiter la balance du compte client).
          bank.processOrder(order1);
          bank.processOrder(order2);
          System.out.println(bank);
          // 101: Joe First (1999, Paris) balance: 3000
          // 102: Jane Second (2001, Paris) balance: 0
          // 7: James Bond (1960, London) balance: -470
        
    Indiquer ce que vous allez modifier aux niveau du code des ordres et indiquer le code de la méthode processOrder.
    Note : bien sûr les instanceof sont interdits, penser au polymorphisme !

  10. On souhaite ajouter une notion d'audit qui se souvient de tous les ordres qui ont déjà été appliqués par processOrder comme cela on va pouvoir rejeter avec une exception si processOrder est appelée deux fois avec le même objet (au sens de equals).
    On veut aussi que l'audit conserve l'ordre dans lequel les ordres sont appliqués.
    Quelle est la classe Java que l'on va utiliser pour stocker les ordres (attention à la complexité pire cas) ?
    Indiquer le champ à ajouter à la classe Bank.
    Indiquer le nouveau code de processOrder qui rejette les ordres en doublon.

  11. On souhaite ajouter une méthode audit qui prend en paramètre une lambda et qui renvoie une liste de tous les ordres pour lesquels la lambda est valide ; par exemple, pour avoir tous les ordres qui utilisent le numéro de compte 101, on écrira
         System.out.println(bank.audit(order -> order.accountId() == 101));
         // [CreditOrder[amount=1000, accountId=101]]
        
    On peut aussi sélectionner des ordres par rapport à leur montant, par exemple
        var order5 = new DebitATMOrder(20_000, 7, 576_723);
        bank.processOrder(order5);
        System.out.println(bank.audit(order -> order.amount() > 750));
        // [CreditOrder[amount=1000, accountId=101], DebitATMOrder[amount=20000, accountId=7, visaId=576723]]
        

    Indiquer le code que vous devez modifiez ainsi que le code de la méthode audit.

  12. Question bonus : on souhaite obtenir les ordres rangés par le numéro de compte dont ils changent la balance. On va pour cela ajouter une méthode auditByAccount telles que le code suivant fonctionne
          System.out.println(bank.auditByAccount());
          // {101=[CreditOrder[amount=1000, accountId=101]], 7=[DebitATMOrder[amount=500, accountId=7, visaId=567583],
          //     ... DebitATMOrder[amount=20000, accountId=7, visaId=576723]]}
        
    Indiquer le code de la méthode auditByAccount.

  13. Question super bonus : on souhaite ajouter une méthode auditATMDebitByAccount qui permet de renvoyer la somme des débits par ATM rangés par le numéro de compte.
          System.out.println(bank.auditATMDebitByAccount());
          // {7=20500}
        
    Indiquer le code de la méthode auditATMDebitByAccount.
    Note : dans l'exemple ci-dessus, le compte 101 n’apparaît pas car il n'y a pas de débit le concernant.

Exercice 4 - javadoc

java.util.Comparator<T>
  - int compare(T o1, T o2)
    Compares its two arguments for order.

  - static <T, U extends Comparable<U>> Comparator<T> comparing(Function<T,U> keyExtractor)
    Accepts a function that extracts a Comparable sort key from a type T, and returns a Comparator<T>
    that compares by that sort key.

java.util.List<E>
  - void sort(Comparator<E> c)
    Sorts this list according to the order induced by the specified Comparator.

java.util.Map<K,V>
  - V computeIfAbsent(K key, Function<K,V> mappingFunction)
    If the specified key is not already associated with a value (or is mapped to null), attempts to compute its
    value using the given mapping function and enters it into this map unless null.

  - V get(Object key)
    Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.

  - V getOrDefault(Object key, V defaultValue)
    Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key.

  - V merge(K key, V value, BiFunction<V,V,V> remappingFunction)
    If the specified key is not already associated with a value or is associated with null,
    associates it with the given non-null value.

  - V put(K key, V value)
    Associates the specified value with the specified key in this map

  -  V putIfAbsent(K key, V value)
    If the specified key is not already associated with a value (or is mapped to null) associates it with the
    given value and returns null, else returns the current value.

java.util.stream.Collectors
  - static <T, U, A, R> Collector<T,?,R> flatMapping(Function<T,Stream<U>> mapper, Collector<U,A,R> downstream)
    Adapts a Collector accepting elements of type U to one accepting elements of type T by applying a flat mapping
    function to each input element before accumulation.

  - static <T, K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<T,K> classifier)
    Returns a Collector implementing a "group by" operation on input elements of type T, grouping elements according
    to a classification function, and returning the results in a Map.

  - static <T, K, D, A, M extends Map<K, D>> Collector<T,?,M> groupingBy(Function<?T,K> classifier,
                                                                         Supplier<M> mapFactory,
                                                                         Collector<T,A,D> downstream)
    Returns a Collector implementing a cascaded "group by" operation on input elements of type T,
    grouping elements according to a classification function, and then performing a reduction operation on the values
    associated with a given key using the specified downstream Collector

  - static <T, K, A, D> Collector<T,?,Map<K,D>> groupingBy(Function<T,K> classifier, Collector<T,A,D> downstream)
    Returns a Collector implementing a cascaded "group by" operation on input elements of type T,
    grouping elements according to a classification function, and then performing a reduction operation on the values
    associated with a given key using the specified downstream Collector.

  - static Collector<CharSequence,?,String> joining()
    Returns a Collector that concatenates the input elements into a String, in encounter order

  - static Collector<CharSequence,?,String> joining(CharSequence delimiter)
    Returns a Collector that concatenates the input elements, separated by the specified delimiter, in encounter order

  - static Collector<CharSequence,?,String> joining(CharSequence delimiter,
                                                           CharSequence prefix,
                                                           CharSequence suffix)
    Returns a Collector that concatenates the input elements, separated by the specified delimiter, with the specified
    prefix and suffix, in encounter order.

  - static <T> Collector<T,?,Integer> summingInt(ToIntFunction<T> mapper)
    Returns a Collector that produces the sum of a integer-valued function applied to the input elements.
    If no elements are present, the result is 0.

  - static <T, U, A, R> Collector<T,?,R> mapping(Function<T,U> mapper, Collector<U,A,R> downstream)
    Adapts a Collector accepting elements of type U to one accepting elements of type T
    by applying a mapping function to each input element before accumulation.

  - static <T, C extends Collection<T>> Collector<T,?,C> toCollection(Supplier<C> collectionFactory)
    Returns a Collector that accumulates the input elements into a new Collection, in encounter order.
    The Collection is created by the provided factory.

  - static <T> Collector<T,?,List<T>> toList()
    Returns a Collector that accumulates the input elements into a new List. There are no guarantees on the type,
    mutability, serializability, or thread-safety of the List returned; if more control over the returned List is
    required, use toCollection(Supplier).

  - static <T> Collector<T,?,List<T>> toUnmodifiableList()
    Returns a Collector that accumulates the input elements into an unmodifiable List in encounter order.