:: Enseignements :: Master :: M1 :: 2024-2025 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
Slices of bread
|
Vue d'un tableau, classe interne,
inner class, classe anonyme et encore un peu de lambdas
Le but de ce TP est d'écrire un slice (une vue partielle d'un tableau)
pour comprendre les notions de vue, de classe interne et de classe anonyme en Java.
Exercice 1 - Maven
Comme pour le TP précédent, nous allons utiliser Maven avec une configuration (le
pom.xml)
très similaire, ici, nous n'avons pas besoin d'activer les
preview features.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fr.uge.slice</groupId>
<artifactId>slice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>23</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.0</version>
</plugin>
</plugins>
</build>
</project>
Comme précédemment, créer un projet Maven,
au niveau du premier écran, cocher
create simple project
puis passer à l'écran suivant en indiquant
Next.
Pour ce TP, le groupId est
fr.uge.slice , l'artefactId est
slice et
la version est
0.0.1-SNAPSHOT. Puis cliquer sur
Finish.
Exercice 2 - The Slice and The furious
Un (
slice)
est une structure de données classique qui permet de découper "virtuellement" un tableau
en gardant des indices de début et de fin (
from et
to) ainsi qu'un
pointeur sur le tableau.
Une implantation d'une structure de données qui ne stocke pas les éléments
est appelée une
vue. C'est une structure de données plus efficace qu'une liste,
car on ne copie pas les éléments du tableau, mais une vue ne respecte pas le principe d'encapsulation
(les éléments n'appartiennent / ne sont pas de la responsabilité de la vue).
En Java, les slices ne sont pas appelés des slices, mais sont accessibles à travers l'interface
List
en utilisant les méthodes
Arrays.asList(array) et
List.subList(from, to),
c'est ce que nous souhaitons réimplanter (donc on ne va pas utiliser ces méthodes !).
-
En Java, un slice correspond à l'interface suivante
public interface Slice<E> {
int size();
E get(int index);
}
Slice une interface paramétrée par le type d'élément du tableau sur lequel un Slice est créé.
Les éléments peuvent ou on être null.
Voici un exemple d'utilisation d'un Slice
String[] array = new String[] { "foo", "bar", "baz", "whizz" };
Slice<String> slice = Slice.of(array, 1, 3);
System.out.println(slice.size()); // 2
System.out.println(slice.get(0)); // bar
System.out.println(slice.get(1)); // baz
La méthode of permet de créer un slice sur un tableau avec les paramètres from et to,
qui sont respectivement l'index du premier élément du slice et l'index après le dernier élément.
Dit différemment, un slice correspond aux éléments dans l'intervalle [from, to[
(from inclus et to exclu).
Rappeler pourquoi doit-on déclarer encore une fois E en début de la méthode of
static <E> Slice<E> of(E[] elements, int from, int to) {
...
}
Écrire l'interface Slice et sa méthode of sachant que l'on va declarer l'implantation de
l'interface SliceImpl en tant que classe interne de l'interface.
Vérifier que les tests marqués "Q1" passent.
Note : pour les préconditions, il existe les méthodes java.util.Objects.checkIndex() et
Objects.checkFromToIndex().
-
On souhaite pouvoir afficher le contenu d'un slice avec la même syntaxe que pour une List.
var array = new String[] { "foo", "bar", "baz", "whizz" };
var slice = Slice.of(array, 1, 3);
System.out.println(slice); // [bar, baz]
Sachant qu'il existe une méthode java.util.Arrays.stream(array, start, end),
implanter la méthode d'affichage en utilisant un stream.
Vérifier que les tests marqués "Q2" passent.
-
On souhaite écrire une méthode subSlice(from, to) qui renvoie un slice qui est une sous-partie
du slice sur lequel on appelle la méthode subSlice.
Par exemple
var array = new String[]{ "foo", "bar", "baz", "whizz" };
var slice = Slice.of(array, 1, 4);
var subSlice = slice.subSlice(1, 2);
System.out.println(subSlice.size()); // 1
System.out.println(subSlice.get(0)); // "baz"
Dans l'exemple ci-dessous, on crée un slice sur le tableau avec l'intervalle [1, 4[, puis on crée un second
slice sur le premier slice avec l'intervalle [1, 2[. C'est équivalent à créer un slice sur [2, 3[
sur le tableau.
Implanter la méthode subSlice de telle façon que le code ci-dessus fonctionne.
Vérifier que les tests marqués "Q3" passent.
-
On souhaite maintenant implanter une méthode reversed qui renvoie un nouveau slice (une vue)
qui permet de voir les éléments en sens inverse (sans copier les éléments).
Voici un exemple d'utilisation
var array = new String[]{ "foo", "bar", "baz", "whizz" };
var slice = Slice.of(array, 1, 4);
var reversed = slice.reversed();
System.out.println(reversed.size()); // 3
System.out.println(reversed.get(0)); // whizz
System.out.println(reversed.get(1)); // baz
System.out.println(reversed.get(2)); // bar
On peut remarquer que pour renverser un slice, il n'est pas nécessaire de connaître l'implantation
si on a accès aux méthode size et get(index).
On peut donc implanter la méthode reversed sous forme de méthode par défaut directement
dans l'interface.
Implanter la méthode reversed sachant qu'en termes d'implantation, nous allons utiliser
une classe anonyme et que pour l'instant, on ne va pas implanter la méthode subSlice(from, to)
Vérifier que les tests marqués "Q4" passent.
Note : en Java, lorsqu'une méthode abstraite est déclarée dans l'interface, on doit l'implanter.
Mais si on ne veut pas l'implanter tout de suite (comme c'est demandé ici), on peut faire qu'elle
lève une exception. Habituellement, on utilise l'exception UnsupportedOperationException pour cela.
Optimisation : si on reversed() un slice déjà reversed() on obtient le slice original.
-
On souhaite ajouter à l'interface Slice une méthode replaceAll qui permet
de remplacer chaque élément en appelant une fonction avec l'ancienne valeur de l'élément,
la fonction renvoyant la nouvelle valeur de l'élément.
Par exemple, pour ajouter des étoiles autour des chaînes de caractères, on peut écrire
var array = new String[]{ "foo", "bar", "baz", "whizz" };
var slice = Slice.of(array, 1, 4);
slice.replaceAll(x -> "*" + x + "*");
System.out.println(slice.get(0)); // *bar*
System.out.println(slice.get(1)); // *baz*
System.out.println(slice.get(2)); // *whizz*
Quelle est l'interface fonctionnelle que l'on doit utiliser en paramètre de replaceAll ?
Quelle est le type de retour de la méthode replaceAll ?
Ajouter la méthode replaceAll à l'interface Slice et modifier les implantations
en conséquence.
Vérifier que les tests marqués "Q5" passent.
-
On souhaite maintenant implanter la méthode subSlice(from, to) quand le Slice
est reversed.
Par exemple
var array = new String[]{ "foo", "bar", "baz", "whizz" };
var slice = Slice.of(array, 1, 4);
var reversed = slice.reversed();
var subSlice = reversed.subSlice(1, 3);
System.out.println(subSlice.get(0)); // baz
System.out.println(subSlice.get(0)); // bar
Implanter la méthode subSlice(from, to) dans la classe anonyme dans la méthode reversed().
Vérifier que les tests marqués "Q6" passent.
Note : vous avez le droit de prendre un papier pour comprendre comment calculer les index correctement.
-
Si vous ne l'avez pas déjà fait, on a oublié d'implanter la méthode d'affichage dans le cas où le
Slice est reversed().
Ajouter cette méthode
Vérifier que les tests marqués "Q7" passent.
-
[Revision] Enfin, pour les plus balèzes, on souhaiterait pouvoir créer un slice juste en passant
des éléments séparés par des virgules.
Par exemple
var slice = Slice.of("foo", "bar", "baz", "whizz");
System.out.println(slice.size()); // 4
Implanter la méthode of sachant que les tests doivent compiler sans warnings
(et non changer les tests n'est pas la solution !).
Vérifier que les tests marqués "Q8" passent.
© Université de Marne-la-Vallée