public interface ActionClass<T> {
//Depuis Java 9 les méthodes statiques sont autorisées
static String getInterfaceDescription() {return "Une interface qui impose de définir une action et permet de l'exécuter une liste de <T>";}
void action(T t);
default void action(List<T> list) {
for(T t:list) action(t);
}
}
Misc après Java 8
Les interfaces en général
A partir de Java 8, il est possible de définir une implantation par défaut dans une interface ainsi que des méthodes statiques.
//Une classe qui affiche une String en majuscule et s'applique automatiquement à une liste de String
public class UpperCaser implements ActionClass<String> {
public void action(String s) {System.out.println(s.toUpperCase());}
}
//Une classe qui affiche la concaténation du nom et du prénom d'une personne et s'applique automatiquement à une liste de Personne
public class PersonFullname implements ActionClass<Person> {
public void action(Person p) {System.out.println(p.prenom()+" "+p.nom());}
}
//La factory .of() est apparue dans Java9 pour créer des Collections non mutables.
new UpperCaser().action(List.of("abc","def"));
new PersonFullname().action(personnes);
System.out.println(ActionClass.getInterfaceDescription());
ABC
DEF
Pierre Durand
Marie Durand
Albert Martin
Une interface qui impose de définir une action et permet de l'exécuter une liste de <T>
Albert Martin
Une interface qui impose de définir une action et permet de l'exécuter une liste de <T>
NPE et Optional<T>
Pour traiter plus simplement les possibles tests des cas de valeurs nulles, Java8 introduit le type Optional
, un conteneur qui fournit des méthodes utilitaires dédiées.
La factory of
vérifie que la référence n’est pas nulle et provoque une exception à la création sinon.
La méthode Depuis Java 9, on préfére l’utilise de get()
retourne la valeur.orElseThrow()
qui émet une NoSuchElementException
en l’abscence de valeur.
String str1 = "value1";
<String> optional1 = Optional.of(str1);
Optional.orElseThrow(); optional1
value1
String str1Bis = null;
<String> optional1 = Optional.of(str1Bis); Optional
EvalException: null
Execution exception
---------------------------------------------------------------------------
java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Objects.java:233)
at java.base/java.util.Optional.of(Optional.java:113)
at .(#69:1)
Il est possible de manipuler des références éventuellement nulles avec la factory ofNullable()
.
String str2 = "value2";
<String> optional2 = Optional.ofNullable(str2);
Optional.orElseThrow(); optional2
value2
String str3 = null;
<String> optional3 = Optional.ofNullable(str3);
Optional.orElseThrow(); optional3
EvalException: No value present
Execution exception
---------------------------------------------------------------------------
java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.orElseThrow(Optional.java:377)
at .(#105:1)
Dans ce cas OrElse()
est une alternative à pour obtenir une valeur par défault en cas de valeur nulle au lieu d’une Exception.
String str3Bis = null;
<String> optional3Bis = Optional.ofNullable(str3Bis);
Optional.orElse("defaultValue3"); optional3Bis
defaultValue3
OrElseGet(Supplier s)
est une variante plus efficace si le calcul de la valeur par défaut est complexe car alors la fonction n’est évaluée qu’en cas de valeur nulle.
Dans l’exemple ci dessous en cas de valeur nulle, une entrée alétoire du tableau est retournée.
String str4 = null;
//String str4 = "value3";
String[] values={"a1","b2","c3"};
<String> optional4 = Optional.ofNullable(str4);
Optional
.orElseGet( () -> values[new Random().nextInt(values.length)] ); optional4
c3
Modularisation
Java 9 introduit la notion de module. Un module regroupe des classes, permet de définir des dépendances entre modules et défini une API publique. Le JDK devient modulaire et les programmes utilisateurs peuvent l’être cela permet d’executer des programmes en consommant moins de mémoire. Les classes internes du JDK ne sont plus accessibles depuis l’application pour améliorer la sécurité.
Plus de détails : https://openjdk.java.net/projects/jigsaw/quick-start
Process
L’accès aux informations des processus systèmes est amélioré. Les examples suivants illustre l’accès au processus de la JVM et à tous ceux du système.
= ProcessHandle.current();
ProcessHandle processHandle .Info processInfo = processHandle.info();
ProcessHandle
System.out.println("PID: " + processHandle.pid());
System.out.println("Arguments: " + processInfo.arguments());
System.out.println("Command: " + processInfo.command());
System.out.println("Instant: " + processInfo.startInstant());
System.out.println("Total CPU duration: " + processInfo.totalCpuDuration());
System.out.println("User: " + processInfo.user());
PID: 39089
Arguments: Optional[[Ljava.lang.String;@1a12b9f7]
Command: Optional[/home/jovyan/.sdkman/candidates/java/21.0.4-tem/bin/java]
Instant: Optional[2024-10-01T16:25:39.550Z]
Total CPU duration: Optional[PT22.79S]
User: Optional[jovyan]
Instant: Optional[2024-05-23T21:04:02.890Z]
Total CPU duration: Optional[PT6.67S]
User: Optional[jovyan]
import java.util.stream.Stream;
<ProcessHandle> liveProcesses = ProcessHandle.allProcesses();
Stream
.filter(ProcessHandle::isAlive).limit(3)
liveProcesses.forEach(ph -> {
System.out.println("PID: " + ph.pid());
System.out.println(" Start: " + ph.info().startInstant());
System.out.println(" User: " + ph.info().user());
});
PID: 1
Start: Optional[2024-09-30T15:21:34.640Z]
User: Optional[root]
PID: 6
Start: Optional[2024-09-30T15:21:34.690Z]
User: Optional[root]
PID: 48
Start: Optional[2024-09-30T15:21:36.300Z]
User: Optional[jovyan]
User: Optional[root]
PID: 6
Start: Optional[2024-05-23T20:48:39.900Z]
User: Optional[root]
PID: 50
Start: Optional[2024-05-23T20:48:45.160Z]
User: Optional[jovyan]
//TODO : ProcessBuilder (Since Java 7)
Inférence du type des variables locales
Depuis Java 10, le type des variables locales avec initialisation peut être inféré à la compilation avec le mot clé var
.
var i = 3;
var myMap = new HashMap<Integer,String>();
.put(1,"A");
myMap.put(2,"B");
myMap; myMap
{1=A, 2=B}
Depuis Java 11, c’est possible dans les lambdas. Le type des paramètre est déjà inféré mais cela permet d’associer des annotations sans avoir à spécifier le type.
public @interface MyAnnotation { }
import java.util.function.BiFunction;
<String,String,String> f1 = (s1, s2) -> s1 + s2;
BiFunction
<String,String,String> f2 = (@MyAnnotation var s1, @MyAnnotation var s2) -> s1 + s2; BiFunction
Instance Of
Depuis Java 12 en preview, l’opérateur instance of
permet de faire implicement un cast en cas de succès.
Object obj = new Person("pierre.durand@a.fr","Pierre", "Durand", 20);
if (obj instanceof Person p) {
System.out.println(p.prenom());
}
Pierre
Text blocks
Depuis Java 13, il est possible d’utiliser des litéraux multilignes pour les String.
String person1Json="""
{
"fistname" : "%s",
"lastname" : "%s",
"age" : %d,
"description" : "lorem ipsum.............\
..................\
..................lorem ipsum"
}
""".formatted("Pierre", "Durand", 12);
; person1Json
{
"fistname" : "Pierre",
"lastname" : "Durand",
"age" : 12,
"description" : "lorem ipsum.................................................lorem ipsum"
}
Records
Les record
(depuis Java 14/15) permettent de définir très simplement des classes non mutables avec constructeurs, accesseurs (sans le préfixe get), toString().
public record Dog(int id, String name){ };
= new Dog(1, "Rex");
Dog d1 = new Dog(2, "Médor");
Dog d2 = new Dog(1, "Rex");
Dog d3
System.out.println(d1+"-> nom:"+d1.name());
Dog[id=1, name=Rex]-> nom:Rex
ainsi qu’une implantation de equals
et hashcode
(égalités de tous les composants). Il sont typement très utilisés pour les design pattern comme le DTO.
import java.util.stream.*;
System.out.println(Stream.of(d1,d2,d3).collect(Collectors.toCollection(HashSet::new)).toString());
[Dog[id=2, name=Médor], Dog[id=1, name=Rex]]
Il est possible de redéfinir ou de surcharger le constructeur et les accesseurs/modificateurs et d’ajouter des variables et méthodes de classes mais pas de variable d’instance.
public record User(int id, String firstname){
public User {
if(id < 0) { throw new IllegalArgumentException("Id has to be greater than 0.");
}
}
};
new User(-1,"Pierre");
EvalException: Id has to be greater than 0.
Execution exception
---------------------------------------------------------------------------
java.lang.IllegalArgumentException: Id has to be greater than 0.
at User.<init>(#130:7)
at .(#132:1)
Switch
A partir de Java 17 le switch peut évaluer une expression et retourner sa valeur.
public enum Day { SUNDAY, MONDAY, TUESDAY,
, THURSDAY, FRIDAY, SATURDAY; }
WEDNESDAY
= Day.WEDNESDAY;
Day day //Day day = Day.TUESDAY;
int l =
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> throw new IllegalStateException("Invalid day: " + day);
};
; l
9
et utiliser des Pattern de classes.
record Dog(int id, String name) {};
record User(int id, String firstname) {};
= new Dog(1, "Rex");
Dog d1
= new User(1,"Pierre");
User u1
public String check(Object obj) {
return switch (obj) {
case User u -> "I'm a user named "+u.firstname();
case Dog d -> "I'm a dog named "+d.name();
case null -> "NULL !";
default -> "A simple object: "+obj;
};
}
System.out.println("-->"+check(d1));
System.out.println("-->"+check(u1));
System.out.println("-->"+check(null));
System.out.println("-->"+check(new Integer(12)));
-->I'm a dog named Rex
-->I'm a user named Pierre
-->NULL !
-->A simple object: 12
Record Pattern
En preview dans Java 19.
public static void printName(Object o) {
if (o instanceof Dog(var id, var name))
System.out.println("Dog %d name is %s.".formatted(id,name));
else
if (o instanceof User(var id, var firstname))
System.out.println("User %d firstname is %s.".formatted(id,firstname));
}
printName(new Dog(3,"Félix"));
printName(new User(1,"Pierre"));
Dog 3 name is Félix.
User 1 firstname is Pierre.