Programmation 2 : septième cours
14 ou 26 novembre 2018
Structure d’un projet et paquets
Structure d’un projet
En Java, un projet peut être découpé en paquets (package
).
Les paquets permettent de :
- associer des classes afin de mieux organiser le code
- de créer des modules indépendants réutilisables
- d’avoir plusieurs classes qui possèdent le même nom (du moment qu’elles ne sont pas dans le même paquet)
Un paquet (package
) :
- est une collection de classes
- peut contenir des sous-paquets
Lors de l’exécution…
Java utilise l’arborescence de fichier pour retrouver les fichiers .class
- Une classe (ou une interface) correspond à un fichier
.class
- Un dossier correspond à un paquet
Les .class
du paquet com.univ_amu
doivent :
- être dans le sous-dossier com/univ_amu
- le dossier com doit être à la racine d’un des dossiers du
ClassPath
Le ClassPath
inclut :
- le répertoire courant
- les dossiers de la variable d’environnement CLASSPATH
- des archives JAR
- des dossiers précisés sur le ligne de commande de java (
-classpath path
)
Lors de la compilation… (1/2)
Le mot-clé package
permet de préciser le paquet des classes ou interfaces définies dans le fichier :
Java utilise l’arborescence pour retrouver le code des classes ou interfaces :
- Une classe (ou une interface)
MyClass
est cherchée dans le fichierMyClass.java
- Le fichier
MyClass.java
est cherché dans le dossier associé au paquet deMyClass
Lors de la compilation… (2/2)
Dans l’exemple précédent, il est donc conseillé que le fichier :
- se nomme
MyClass.java
- se trouve dans le dossier
com/univ_amu
(Par défaut, la compilation créeMyClass.class
danscom/univ_amu
)
Utilisation d’une classe à partir d’un autre paquet
Accessibilité :
- Seules les classes publiques sont utilisable à partir d’un autre paquet
- Un fichier ne peut contenir qu’une seule classe publique
On peut désigner une classe qui se trouve dans un autre paquet :
package com.my_project;
public class Main {
public static void main(String[] args) {
com.univ_amu.MyClass myClass =
new com.univ_amu.MyClass();
}
}
Importer une classe
Vous pouvez également importer une classe :
package com.my_project;
import com.univ_amu.MyClass;
public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
}
}
Deux classes de deux paquets différents peuvent avoir le même nom :
- Exemple :
java.util.List
etjava.awt.List
- Attention de choisir le bon import
Importer un paquet
Vous pouvez également importer toutes les classes d’un paquet :
package com.my_project;
import com.univ_amu.*;
public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
}
}
Remarques :
- Les classes des sous-paquets ne sont pas importées
- Le paquet
java.lang
est importé par défaut
Importer des membres statiques
Depuis Java 5, il est possible d’importer directement des méthodes ou attributs de classes (static
).
La syntaxe est la suivante :
Exemple :
Exemple sans import statique
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
System.out.println(
"A circle with a diameter of 5 cm has");
System.out.println("a circumference of "
+ (Math.PI * 5) + " cm");
System.out.println("and an area of "
+ (Math.PI * Math.pow(2.5, 2))
+ " sq. cm");
}
}
Exemple d’import statique
import static java.lang.Math.PI;
import static java.lang.Math.pow;
import static java.lang.System.out;
public class HelloWorld {
public static void main(String[] args) {
out.println("Hello World!");
out.println(
"A circle with a diameter of 5 cm has");
out.println("a circumference of "
+ (PI * 5) + " cm");
out.println("and an area of "
+ (PI * pow(2.5, 2)) + " sq. cm");
}
}
Un exemple
Le fichier com/univ_amu/HelloWorld.java
:
package com.univ_amu;
public class HelloWorld {
public static void main(String[] arg) {
System.out.println("Hello world ! ");
}
}
$ javac com/univ_amu/HelloWorld.java
$ ls com/univ_amu
HelloWorld.java HelloWorld.class
$ java com.univ_amu.HelloWorld
Hello world !
Quelques remarques
Nommage des paquets :
- Les noms de paquets sont écrits en minuscules
- Pour éviter les collisions, on utilise le nom du domaine à l’envers ⇒
com.google.gson
,com.oracle.jdbc
- Si le nom n’est pas valide, on utilise des underscores : ⇒
com.univ_amu
Fichier JAR (Java Archive) :
- est une archive ZIP pour distribuer un ensemble de classes Java
- contient un manifest (qui peut préciser la classe qui contient le main)
- peut également faire partie du
ClassPath
- peut être généré en ligne de commande (
jar
) ou avec un IDE
Classe interne
Classe imbriquée statique
Il est possible de définir une classe à l’intérieur d’une autre (classe imbriquée ou nested class) :
public class LinkedList {
public static class Node {
private String data; private Node next;
public Node(String data, Node next) {
this.data = data; this.next = next;
}
}
}
Il est possible d’instancier la classe interne sans qu’une instance de LinkedList
existe car elle est statique :
Classe imbriquée statique
Rappel
Une classe non-imbriquée publique (public
) doit être dans un fichier portant son nom.
Interdit !
Fichier LinkedList.java
⇒ erreur à la compilation :
Remarque
Une classe imbriquée peut être publique et accessible depuis l’extérieur.
Classe imbriquée statique
Il est également possible de la rendre privée à la classe LinkedList
:
public class LinkedList {
private static class Node {
private String data;
private /*LinkedList.*/Node next;
public Node(String data, Node next) {
this.data = data;
this.next = next;
}
}
}
Dans ce cas, seules les méthodes de LinkedList
pourront l’utiliser. Notez que des méthodes statiques définies dans LinkedList
peuvent également utiliser cette classe interne du fait qu’elle soit statique.
Classe imbriquée statique
Exemple d’implémentation de méthodes dans la classe LinkedList
:
public class LinkedList {
/* Code de la classe interne statique Node. */
private Node first = null;
public void add(String data) {
first = new Node(data, first);
}
public void print() {
Node node = first;
while (node!=null) {
System.out.print("["+node.data+"]");
node = node.next;
}
}
}
Classe imbriquée statique
Exemple d’utilisation de la classe précédente :
Cet exemple produit la sortie suivante :
[c][b][a]
Classe imbriquée statique
Une classe imbriquée statique ne peut accéder qu’aux champs et méthodes statiques de la classe qui la contient :
public class LinkedList {
private static class Node {
/* Champs et méthodes de Node. */
boolean isFirst() {
return this==first; // interdit !
}
}
private Node first;
/* Autres champs et méthodes de LinkedList. */
}
Classe interne
En revanche, si la classe interne n’est pas statique, elle peut accéder aux champs de classe qui la contient :
public class LinkedList {
private class Node {
/* Champs et méthodes de Node. */
private boolean isFirst() {
return this==first; // autorisé !
}
}
private Node first;
/* Autres champs et méthodes de LinkedList. */
}
Classe interne
Java insère dans l’instance de Node
une référence vers l’instance de LinkedList
qui a permis de la créer :
public class LinkedList {
private class Node {
/* Champs et méthodes de Node. */
private boolean isFirst() {
return this==/*référenceVersLinkedList.*/first;
}
}
public void add(String data) {
first = new Node(data, first);
}
/* Autres champs et méthodes de la classe LinkedList. */
}
Classe interne
Il est possible d’utiliser la méthode isFirst
dans LinkedList
:
public class LinkedList {
/* Code de la classe interne statique Node
et champs et méthodes de la classe LinkedList. */
public void print() {
Node node = first;
while (node!=null) {
System.out.print("["+node.data
+","+node.isFirst()+"]");
node = node.next;
}
}
}
Classe interne
Exemple d’utilisation de la classe précédente :
Cet exemple produit la sortie suivante :
[c,true][b,false][a,false]
Types paramétrés (notions avancées)
Condition sur les paramètres – Problématique
class Greatest {
private String element;
public void add(String element) {
if (this.element==null ||
this.element.compareTo(element)<0)
this.element = element;
}
public String get() { return element; }
}
Comment rendre la classe Greatest
générique ?
Condition sur les paramètres
class Greatest<T extends Comparable<T>> {
private T element;
public void add(T element) {
if (this.element==null
|| element.compareTo(this.element)>0)
this.element = element;
}
public T get() {
return element;
}
}
Problématique
Supposons que nous ayons les classes suivantes :
class Greatest<T extends Comparable<T>> {
/* ... */
public void add(T element) { /* ... */ }
public T get() { return element; }
}
class Card implements Comparable<Card> { /* ... */ }
class PrettyCard extends Card { /* ... */ }
Il n’est pas possible d’écrire les lignes suivantes car PrettyCard n’implémente pas l’interface Comparable<PrettyCard>
:
Greatest<PrettyCard> greatest =
new Greatest<PrettyCard>();
greatest.add(new PrettyCard(Card.diamond, 7));
Syntaxe ? super
Supposons que nous ayons les classes suivantes :
class Greatest<T extends Comparable<? super T>> {
/* ... */
public void add(T element) { /* ... */ }
public T get() { return element; }
}
Il est possible d’écrire les lignes suivantes car PrettyCard
implémente l’interface Comparable<Card>
et Card super PrettyCard
:
Greatest<PrettyCard> greatest =
new Greatest<PrettyCard>();
greatest.add(new PrettyCard(Card.diamond, 7));
? extends – Problématique
Supposons que nous ayons les classes suivantes :
class Greatest<T extends Comparable<? super T>> {
/* ... */
public void add(T element) { /* ... */ }
public void addAll(List<T> list) {
for (T element : list) add(element);
}
}
Il n’est pas possible d’écrire les lignes suivantes :
List<PrettyCard> list = new ArrayList<PrettyCard>();
Greatest<Card> greatest = new Greatest<Card>();
/* ... */
list.addAll(list);
? extends
Supposons que nous ayons les classes suivantes :
class Greatest<T extends Comparable<? super T>> {
/* ... */
public void add(T element) { /* ... */ }
public void addAll(List<? extends T> list) {
for (T element : list) add(element);
}
}
Il est maintenant possible d’écrire les lignes suivantes :
List<PrettyCard> list = new ArrayList<PrettyCard>();
Greatest<Card> greatest = new Greatest<Card>();
/* ... */
greatest.addAll(list);
Méthodes paramétrées et conditions sur les types
class Tools {
static <T extends Comparable<T>>
boolean isSorted(T[] array) {
for (int i = 0; i < array.length-1; i++)
if (array[i].compareTo(array[i+1]) > 0)
return false;
return true;
}
}
Exemple :
Méthodes paramétrées et conditions sur les types
Méthode pour copier une liste src
vers une autre liste dest
:
static <T> void copy(List<? super T> dest, List<? extends T> src)
On suppose qu’on a une classe MovingPixel
qui étend Pixel
qui elle-même étend Point
.
On peut écrire :
List<MovingPixel> src = new ArrayList<>();
List<Point> dest = new ArrayList<>();
Collections.<Pixel>copy(dest, src);
Utilisation extends
et super
Lorsqu’on a une collection d’objets de type T
:
En entrée/écriture, on veut donner des objets qui ont au moins tous les services des objets de type
T
.On doit donc donner des objets dont la classe étend
T
:? extends T
En sortie/lecture, on veut récupérer des objets qui ont au plus tous les services des objets de type
T
.On doit donc récupérer des objets qui sont étendu par la classe
T
:? super T
Interfaces (notions avancées)
Les classes anonymes
Supposons que nous ayons l’interface suivante :
Il est possible de :
- définir une classe anonyme qui implémente cette interface
- d’obtenir immédiatement une instance de cette classe
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
counter++;
}
});
Les classes anonymes
public class Window {
private int counter;
public Window() {
Button button = new Button("count");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
counter++;
}
});
}
}
Les classes anonymes
Il est possible d’utiliser des attributs de la classe “externe” :
public class Window {
private Counter counter = new Counter();
public Window() {
Button button = new Button("count");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
counter.count();
}
});
}
}
Les classes anonymes
Il est possible d’utiliser des variables finales de la méthode :
public class Window {
public Window() {
final Counter counter = new Counter();
Button button = new Button("count");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
counter.count();
}
});
}
}
Java 8 : Lambda expressions
Avec Java 8, il est possible d’écrire directement :
public class Window {
public Window() {
Button button = new Button("button");
button.addActionListener(
event -> System.out.println(event)
);
}
}
Explication : ActionListener possède une seule méthode donc on peut affecter une lambda expression à une variable de type ActionListener.
Interfaces fonctionnelles
En Java 8, une interface n’ayant qu’une méthode abstraite est une interface fonctionnelle. Les quatre interfaces fonctionnelles suivantes (et plein d’autres) sont déjà définies :
public interface Predicate<T> {
public boolean test(T t);
}
public interface Function<T,R> {
public R apply(T t);
}
public interface Consumer<T> {
void accept(T t);
}
public interface Comparator<T> {
int compare(T o1, T o2);
}
Syntaxe d’une lambda expression
Pour instancier une interface fonctionnelle, on peut utiliser une lambda expression :
L’interface suivante :
peut être instancier par :
MyFunctionalInterface fonc =
(arg1, arg2, arg2)
->/* expression définissant le résultat de myMethod */
Si T
est void
alors l’expression peut être void
comme un println
.
Exemples de lambda expression
On considère une classe Person
avec deux attributs name
et age
et les getters et setters associés.
On a le droit d’écrire les lambda expressions suivantes en Java :
person -> person.getAge() >= 18
de typePredicate<Person>
person -> person.getName()
de typeFunction<Person,String>
name -> System.out.println(name)
de typeConsumer<Person>
Remarques
- Il n’est pas nécessaire de mettre le type des paramètres.
- On peut omettre les parenthèses dans le cas où il n’y a qu’un seul paramètre
Référence de méthodes
Dans un certain nombre de cas, une lambda expression se contente d’appeler une méthode ou un constructeur.
Il est plus clair dans ce cas de se référer directement à la méthode ou au constructeur.
Lambda expression | référence de méthode |
---|---|
x -> Math.sqrt(x) |
Math::sqrt |
name -> System.out.println(name) |
System.out::println |
person -> person.getName() |
Person::getName |
name -> new Person(name) |
Person::new |
java.util.stream.Stream
Stream
= Abstraction d’un flux d’éléments sur lequel on veut faire des calculs
Ce n’est pas une Collection
d’élément car un Stream
ne contient pas d’élément
Création d’un Stream :
- À partir d’une collection comme une liste avec
list.stream()
- À partir d’un fichier :
Files.lines(Path path)
- À partir d’un intervalle :
IntStream.range(int start, int end)
Exemples d’utilisation
persons
.stream()
.filter(person -> person.getAge() >= 18)
.map(person -> person.getName())
.forEach(name -> System.out.println(name));
Types des paramètres et retours des méthodes :
stream()
→Stream<Person>
filter(Predicate<Person>)
→Stream<Person>
map(Function<Person, String>)
→Stream<String>
forEach(Consumer<String>)
Opérations intermédiaires possibles sur un Stream
Stream<E> filter(Predicate<? super E>)
: sélectionne si un élement reste dans leStream
<R> Stream<R> map(Function<? super E, ? extends R)
: transforme les éléments duStream
en leur appliquant une fonctionStream<E> sorted(Comparator<? super E>)
: trie les éléments
Opérations terminales possibles sur un Stream
long count()
: compte le nombre d’élémentslong sum()
: somme les éléments (entiers ou double)Stream<E> forEach(Consumer<? super E>)
: Appele leconsumer
pour chaque élémentallMatch(Predicate<? super E>)
: vrai si le prédicat est vrai pour tous les élémentsanyMatch(Predicate<? super E>)
: vrai si le prédicat est vrai pour au moins un élémentcollect(Collectors.toList())
: crée une liste avec les éléments duStream