:: Enseignements :: Master :: M1 :: 2015-2016 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Examen de Java Avance sur table |
Exercice 1 - Questions de cours (6)
Répondez aux questions suivantes en deux ou trois phrases, pas plus.
-
Pourquoi il n'est pas possible en Java d'avoir des tableaux de types paramétrés ?
-
Qu'affiche le code suivant ?
Integer i = 300;
Integer j = 300;
System.out.println(i == j);
Attention il y a un piègle !
-
Qu'affiche le code ci-dessous ? Pourquoi ?
public static void main(String[] args) {
String s = "hello";
s.toUpperCase();
System.out.println(s);
}
Attention il y a un piège !
-
Le code suivant compile-t-il ? Pourquoi ?
Si le code compile, qu'affiche-t-il ? Pourquoi ?
interface Main {
class A {
void m() { System.out.println("1"); }
}
class B extends A {
public void m() { System.out.println("2"); }
}
public static void main(String[] args) {
A a = new B();
a.m();
}
}
-
Dans le code précédent, si la méthode m de la classe A est déclarée private, le code compile-t-il ? Pourquoi ? Si le code compile, qu'affiche-t-il ? Pourquoi ?
-
Dans le code ci-dessous, la méthode f de A est gentiment récursif, sans test d'arrêt.
En fait, ce que l'on veut, c'est que la méthode f de A rappelle la méthode f de I. Comment faire cela en Java ?
interface I {
default int f() {
return ...; // on suppose qu'il y a une implantation
}
}
class A implements I {
int f() {
return f() + 1;
}
}
Exercice 2 - Exceptionnellement (4)
Vendredi dernier, après un pot un peu trop arrosé pour le départ de l'un de vos collègues,
vous avez écrit le code suivant:
public class Locker {
private ReentrantLock lock = new ReentrantLock();
public R doLocked(Supplier<R> supplier) {
R rValue;
lock.lock();
rValue = supplier.get();
lock.unlock();
return rValue;
}
}
public class Main {
public static void main(String[] args) {
Locker locker = new Locker();
String message = locker.doLocked(() -> {
return "hello";
});
System.out.println(message);
}
}
Le code de
Locker était censé garantir que le traitement passé en paramètre de
doLocked
ne pouvait être fait que par une seule thread à la fois (pour un même objet
Locker)... Mais le lundi, en redécouvrant le code de la classe
Locker,
vous vous promettez de respecter l'adage, "boire ou coder, il faut choisir !"
-
La signature de la méthode doLocked est fausse, il y a deux problèmes dans la
façon dont la méthode paramétrée est déclarée. Indiquez quelle est la bonne signature.
-
Le code de la classe Locker a deux bugs de concurrence. Corrigez-les en indiquant
le nouveau code.
-
Sachant que Files.getLastModifiedTime() peut lever l'exception IOException,
expliquez pourquoi le code ci-dessous ne compile pas.
public static FileTime lastTime(Locker locker, Path path) throws IOException {
return locker.doLocked(() -> {
return Files.getLastModifiedTime(path);
});
}
-
Modifiez le code de lastTime pour qu'il compile (sans modifier le code de Locker)
et, bien sûr, qu'il marche correctement.
Exercice 3 - Une Liste, ça crée des liens (10)
On cherche à écrire une classe
Link représentant une liste chaînée,
avec quelques opérations sympathiques.
Il y a plusieurs représentations possibles des listes chaînées. Dans cet exercice,
la liste chaînée sera manipulée par son premier maillon, donc
Link
représente à la fois la liste chaînée et un maillon.
La structure de données sera donc équivalente à cette déclaration Java :
class Link {
Object element; // élément du maillon
Link next; // référence sur l’élément suivant
}
Le champs
next contient
null s'il s'agit du dernier maillon.
De plus, la liste possède une représentation textuelle, les éléments sont affichés
séparés par le signe " -> ".
Par exemple, une liste ayant deux éléments
element1 et
element2 sera représentée comme ceci:
element1 -> élement2
-
Donnez le code de la classe Link sachant que l'on veut que la liste soit typée par le type d’élément qu'elle peut contenir (une Link<String> si l'on stocke des chaînes de caractères par exemple),
avec ses champs et son constructeur en faisant attention aux modificateurs de visibilités.
-
Donnez le code qui permet de créer la liste chaînée suivante:
baz -> bar -> foo
Attention à l'ordre des éléments !
-
En fait, on ne souhaite pas permettre à l'utilisateur de créer des maillons en
accédant directement au constructeur. Nous allons ajouter deux méthodes à la classe
Link: of et prepend qui permettent respectivement de créer
une liste chaînée avec un seul maillon et d'ajouter un maillon avant la liste chaînée.
Par exemple, la liste chaînée précédente peut être créée avec le code suivant,
si l'on utilise of et prepend.
Link<String> l = Link.of("foo").prepend("bar").prepend("baz");
On peut noter que l'appel à la méthode of se fait sur la classe.
Indiquez le code des méthodes of et prepend.
-
Écrivez la méthode toString.
(en récursif, c'est plus simple !)
-
On cherche à écrire une méthode forEach qui prend en paramètre
une fonction qui sera appelée pour chaque élément.
Indiquez la signature de la méthode forEach (attention au sous-typage !).
Proposez une implantation récursive de la méthode forEach.
Proposez ensuite une implantation itérative de la méthode forEach.
-
Écrivez une méthode iterator qui renvoie un Iterator permettant
de parcourir les éléments de la liste chaînée.
Attention à bien vérifier que les contrats des méthodes de l'Iterator
sont bien respectés.
-
Sachant qu'il est facile de créer un Stream à partir d'un itérateur,
avec le code ci-dessous,
public Stream<E> stream() {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(iterator(),
Spliterator.IMMUTABLE|Spliterator.NONNULL), false);
}
proposez une nouvelle implantation de la méthode toString utilisant la méthode stream().
-
Écrivez une méthode map qui prend en paramètre une fonction et renvoie une nouvelle liste chaînée en appliquant cette fonction à chaque élément de la liste.
Voici un exemple d'utilisation
Link<String> l = Link.of("foo").prepend("bar").prepend("baz");
Link<Integer> l2 = l.map(String::length);
System.err.println(l2); // 3 -> 3 -> 3
Attention aux sous-typages des types paramétrés.
-
Écrivez une méthode filter qui prend en paramètre une fonction et renvoie une nouvelle liste chaînée contenant les éléments de la liste initiale pour lesquels l'appel de cette fonction renvoie vrai.
Voici un exemple d'utilisation:
Link<String> l3 = l.filter(e -> e.length() >= 2 && e.charAt(1) == 'a');
System.err.println(l3); // baz -> bar
-
Écrivez une méthode reduce qui prend en paramètre une valeur initiale pour la valeur accumulée et une fonction à deux paramètres
(valeur accumulée et élément courant) et qui applique la fonction à tous les éléments et renvoie la valeur accumulée.
Puis utilisez la méthode reduce pour écrire une méthode reverse sur une liste qui renverse l'ordre de ses éléments.
Exemple d'utilisation de la méthode reverse:
Link<String> l4 = l.reverse();
System.err.println(l4); // foo -> bar -> baz
© Université de Marne-la-Vallée