améliorations dans java depuis la version 5
TRANSCRIPT
Évolutions du langage Java
Évolutions dans Java SE 5.0
Génériques - Permet un type ou une méthode de fonctionner sur des objets de différents types, tout
en offrant la sécurité de type à la compilation. Ajoute la sécurité de type de la librairie des Collections
Java et élimine la corvée de la gestion du cast
Amélioration de boucle - Cette nouvelle structure du langage, élimine la corvée et la propension
aux erreurs des itérateurs et des variables d'index lors de l'itération sur les collections et les tableaux.
Autoboxing / unboxing – Elimine la corvée de conversion manuelle entre les types primitifs
(comme int) et les types d'encapsulation correspondants (examinés Integer).
Énumérations typesafe – Permet de créer des types énumérés orientés objet avec des méthodes et
des champs arbitraires.
VarArgs – Possibilité de passer à des méthodes une liste d'arguments de longueur variable.
Statique Import – Permet d'éviter la qualification des membres statiques avec des noms de classe.
Annotations (métadonnées) – Cette fonctionnalité du langage permet des traitements à la
compilation ou au runtime du code source (des outils permettent de générer du code à partir des
annotations). Cela permet de faire de la programmation "déclarative" où le programmeur dit ce qui
doit être fait par des annotations et les outils émettent le code pour le faire. Il élimine donc la nécessité
pour le maintien de « fichiers secondaires» qui doivent être tenus à jour avec les changements dans les
fichiers sources. Au contraire, l'information peut être maintenue dans le fichier source.
http://docs.oracle.com/javase/8/docs/technotes/guides/language/enhancements.html#javase8
Évolutions dans Java SE 6
Aucun changement du langue n’a été introduit dans Java SE 6
Évolutions dans Java SE 7
Binary Literals - Avec Java 7 les types primitifs byte , short, int et long peuvent être déclarés en
utilisant la notation binaire base 2. Pour cela il faut utiliser les préfixes 0b ou 0B.
byte aByte = (byte)0b00100001;
short aShort = (short)0b1010000101000101; // A 16-bit 'short' value:
int anInt1 = 0b10100001010001011010000101000101; / / Some 32-bit 'int' values:
// A 64-bit 'long' value. Note the "L" suffix:
long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;
Underscores in Numeric Literals – Le caractère underscore (_) peut apparaître à tout endroit
d’un nombre. Cela peut être utile pour la lisibilité des nombres à manipuler en groupant les séries de
chiffres.
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F; long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
float pi1 = 3_.1415F; //KO cannot put underscores adjacent to a decimal point
float pi2 = 3._1415F; //KO cannot put underscores adjacent to a decimal point
int x3 = 52_; //KO - cannot put underscores at the end of a literal
int x4 = 5____2;//OK - (decimal literal) int x5 = 0_x52; //KO - cannot put underscores in the 0x radix prefix
int x6 = 0x_52; //KO - cannot put underscores at the beginning of a number
int x7 = 0x5_2; //OK - (hexadecimal literal)
int x8 = 0x52_; //KO - cannot put underscores at the end of a number
int x9 = 0_52; //OK - (octal literal) int x10 = 05_2; //OK - (octal literal)
int x11 = 052_; //KO - cannot put underscores at the end of a number
Strings in switch Statements – Utilisation possible des String dans les comparaisons des switch
String typeOfDay;
switch (dayOfWeekArg) {
case "Monday":
typeOfDay = "Start of work week";
break;
case "Tuesday":
…
default:
…
}
La comparaison est case sensitive et s’effectue avec la méthode String.equals().
Type Inference for Generic Instance Creation – Déduction de type)
Avec Java 7 on peut supprimer les types paramétrés quand on fait appel au constructeur des classes
génériques tant que le compilateur peut les déduire. Le pair ‘<>’ est appelé losange.
Exemple :
Avant java 7 :
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
Maintenant :
Map<String, List<String>> myMap = new HashMap<>();
Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning
class MyClass<X> { <T> MyClass(T t) {
// ...
}
}
Avant Java 7 : new MyClass<Integer>(""); // OK
Avec Java 7 : MyClass<Integer> myObject = new MyClass<>("");
Le compilateur déduit le type générique de la classe et le paramètre générique du constructeur.
================================================
Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters
with Varargs Methods – The Java SE 7 complier generates a warning at the declaration site of a
varargs method or constructor with a non-reifiable varargs formal parameter. Java SE 7 introduces the
compiler option -Xlint:varargs and the annotations @SafeVarargs and
@SuppressWarnings({"unchecked", "varargs"}) to suppress these warnings.
================================================
The try-with-resources Statement – Le try-with-resources est une déclaration qui declare un ou
plusieurs ressources qui doit être fermer/libérer une fois quand on ne s’en sert plus.
Le try-with-resources s’assure que chaque ressource est libérée à la fin du try-with-resources. Tout
objet qui implemente les interfaces java.lang.AutoCloseable ou java.io.Closeable peut alors être
considérée comme une ressource.
REMARQUE :
Les classes java.io.InputStream, OutputStream, Reader, Writer, java.sql.Connection, Statement,
and ResultSet ont évoluées pour implémenter l’interface AutoCloseable et peuvent être utilisées dans
une déclaration try-with-resources.
Exemple d'utilisation :
String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
On peut utiliser une ou plusieurs ressources dans un déclaration try-with-resource.
public static void writeToFileZipFileContents(String zipFileName, String outputFileName) throws
java.io.IOException {
java.nio.charset.Charset charset = java.nio.charset.StandardCharsets.US_ASCII;
java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName);
// Open zip file and create output file with try-with-resources statement
try (
java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
// Enumerate each entry
for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
// Get the entry name and write it to the output file
String newLine = System.getProperty("line.separator");
String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
}
NB :
Dans cet exemple deux exceptions peuvent etre levées dans l'instruction try-with-resource (lors des
appels zf.close() et writer.close()).
Si une exception est levée dans le bloc d'instructions et en même temps par la déclaration try-with-
resource alors ces dernières sont tues et seule la première est renvoyée. Pour intercepter ces exceptions
là il faut appeler Throwable.getSuppressed.
Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking
– Avec Java 7 on peut intercepter plusieurs types d'exceptions dans un seul bloc catch.
En outre le compilateur effectue une meilleure analyse des exceptions retransmises par les instructions
throw e; permettant de mieux spécifier les types d'exceptions dans les clauses throws des déclarations
de méthode.
Avant Java 7 :
catch (IOException ex) {
logger.log(ex);
throw ex;
} catch (SQLException ex) {
logger.log(ex);
throw ex;
}
Avec java 7 :
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
Rethrowing Exceptions with More Inclusive Type Checking
Soit le code suivant :
Avant Java 7 :
public void rethrowException(String exceptionName)
throws Exception {
try {
if (exceptionName.equals("First")) {
throw new FirstException();
} else {
throw new SecondException();
}
} catch (Exception e) {
throw e;
}
}
Avec Java 7 :
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e ) { throw e ; } }
Le compilateur vérifie que que l'exception de l'instruction throw e provient du bloc try et que seules
les exceptions FirstException, SecondException y sont levées.
Évolutions dans Java SE 8
1. Lambda Expressions – Permet d’encapsuler une unique unité de travail, une
fonctionnalité ou un comportement et de le passer à un nouveau contexte d’exécution. On peut
utiliser les lambda expressions sur chaque élément d’une collection, à la fin de l’exécution d’un
process ou bien en cas d’erreur d’un process. On peut distinguer trois types de lambdas
expressions:
Method References
Ce sont des lambdas expressions compact, facile à lire ou comprendre car ce sont des méthodes
qui possèdent un nom et donc signature.
Exemple Arrays.sort(rosterAsArray, Person::comparePersonneByAge );
Le tableau ci-dessous résume les différents types de lambdas expressions utilisant des références de
méthodes:
Référence d'une méthode statique
ContainingClass::staticMethod
Arrays.sort(rosterAsArray, Person::compareByAge);
Référence depuis un objet d’un type particulier
containingObject::instanceMethodName
ComparisonProvider compareProvider = new
ComparisonProvider();
Arrays.sort(rosterAsArray,
compareProvider::compareByName);
Référence depuis un type particulier
ContainingType::methodName
String [] stringArray = {"Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda"};
Arrays.sort(stringArray, String::compareToIgnoreCase);
Référence à un constructeur
ClassName::new
--------------------------
Utilisation avec une lambda
Soit l’exemple ci-dessous :
public static <T, SOURCE extends
Collection<T>, DEST extends Collection<T>>
DEST transferElements(
SOURCE sourceCollection,
Supplier<DEST> collectionFactory) {
DEST result = collectionFactory.get();
for (T t : sourceCollection) {
result.add(t);
}
return result;
}
-------------------------------
1 Set<Person> rosterSetLambda =
transferElements(roster, () -> { return new HashSet<>();
});
Avec une référence contructeur (celui de HashSet)
2 Set<Person> rosterSet = transferElements(roster,
HashSet::new);
Le compilateur déduit le type paramètré comme un type
Person à partir du type paramétré dans
Toujours avec une référence de constructeur.
3 Set<Person> rosterSet = transferElements(roster,
HashSet<Person>::new);
Default Methods Permettent l’ajout de nouvelles fonctionnalités aux interfaces des
librairies d’un API et d’assurer la compatibilité avec le code existant pour les anciennes
versions de ces interfaces. Ces méthodes d’interfaces ont une implémentation et un le mot clef
default devant leur signature. En addition on peut ajouter des méthodes statiques dans ces
interfaces.
Définition
En java une interface est un type de référence, semblable à une classe, pouvant contenir uniquement
des constantes, des signatures de méthode, des méthodes statiques, et des types internes. Les
implémentations de méthode existent uniquement pour les méthodes default et les méthodes
statiques. Les interfaces ne peuvent pas être instanciées, ils peuvent seulement être implémentés
(implements) ou étendus (extends) par d’autres interfaces.
Extending Interfaces That Contain Default Methods
Lors de l’extension d’une interface contenant une default method on peut soit :
Ne pas mentionner la méthode default et hériter de celui de la super-interface.
Redéclarer la méthode par défaut, ce qui la rend abstraite (sans default devant la signature).
Ou redéfinir la default method pour réécrire celle de la super-interface. Toute classe qui
l’implémente cette interface utilisera cette default method au lieu de celle de la super-
interface.
Static Methods
Avec Java 8 on peut définir des méthodes statiques dans une interface. La déclaration est identique à
celle d’une classe.
Définition
Une méthode statique est une méthode qui est associée à la classe dans laquelle elle est définie plutôt
qu’à un quelconque objet.
Integrating Default Methods into Existing Libraries
Les default method méthodes permettent d’ajouter de nouvelles fonctionnalités à des interfaces
existantes sans nuire à la compatibilité binaire du code existant se basant sur les versions antérieures
de ces interfaces.
Les default method permettent d’ajouter des méthodes qui acceptent des lambdas expressions comme
paramètre. Cette section montre un exemple d’utilisation de l’intégration des default method et de
méthode statique dans une interface (Exemple de l’API Comparator).
New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in
Java SE 8
En Java SE 8 de nouvelles classes et APIs qui exploitent les lambdas expressions et les Streams (que
l’on verra plus bas) ont été ajoutées. Des classes existantes ont été modifiés et mis à jour pour utiliser
ces nouvelles évolutions.
La p lus part de ces classes se situent dans les packages suivants :
java.util: Contient les interfaces et classes permettant de manipuler les colletions
(Java Collections Framework ) et les Stream.
java.util.function: Nouveau package Contient les interfaces fonctionnelles servant en général de types cibles
(target types) de type cible en général pour les lambdas expressions et
les références de méthode (method references).
java.util.stream: Nouveau package Contient la majorité des interfaces et classes permettant de manipuler les
Stream et les opérations d’agrégation sur les collections (aggregate
operations).
Ci-dessous les classes et leurs packages qui ont été modifiés pour intégrer les évolutions sur les Strem
et lambdas expressions.
Package Nouvelles Classes Classes Modifiées
java.io UncheckedIOException BufferedReader
java.lang
AutoCloseable
ThreadLocal
String
Iterable
CharSequence
Boolean
Integer
Long
Float
Double
java.nio.file Files
java.util
PrimitiveIterator
Spliterator
DoubleSummaryStatistics
IntSummaryStatistics
LongSummaryStatistics
Optional
OptionalDouble
OptionalInt
OptionalLong
Spliterators
SplittableRandom
StringJoiner
Arrays
BitSet
Collection
Comparator
Iterator
List
Map
Map.Entry
LinkedHashMap
Random
TreeMap
java.util.concurrent ThreadLocalRandom
java.util.jar JarFile
java.util.zip ZipFile
java.util.logging Logger
java.util.regex Pattern
2. Improved Type Inference – Déduction de type évoluée à la compilation
Le compilateur peut déduire les différents types cibles des paramètres des méthodes génériques. Le
type cible d’une expression est le type des données auxquelles le compilateur s’attend à recevoir.
Par exemple en Java 7 lors d’une assignation Map<String, String> aMap = new HashMap<>() ;
En Java 8 on peut utiliser la déduction de types des expressions dans des contextes plus variés
Lambdas Expressions ou Invocation de Méthode pour déduire les types des paramètres.
Par exemple le code suivante ne passera pas en Java 7 et antérieur.
List<String> stringList = new ArrayList<>();
stringList.add("A");
stringList.addAll(Arrays.asList());
L’erreur suivante est levée :
The method addAll(Collection<? extends String>) in the type List<String> is not applicable for the
arguments (List<Object>)
Pour résoudre l’erreur il faudra indiquer explicitement le type requis :
stringList.addAll(Arrays.<String>asList());
La méthode addAll(...) prend en paramètre un type Collection< ? extends String> et Arrays.asList()
retourne une liste List<T> .
En Java 8 le compilateur déduit que le type T attendu est de type String grâce au de la méthode
addAll(Collection< ? extends String>)
3. Annotations on Java Types – Annotation de Type
En java Java 7 on peut appliquer les annotations uniquement sur les déclarations de variable, méthode,
et classes.
Java 8 a introduit une évolution sur les annotations appelées Type Annotation. Ces annotations
peuvent être utilisées pour différents type d’usage. On peut utiliser les annotations partout où un
type peut être utilisé, lors de la création d’instances (new), lors d’un cast, dans les clauses throws ou
implements etc.… Ces annotations sont appelées Type Annotation.
Création d’une instance de classe :
new @Interned MyObject(); Type cast:
myString = (@NonNull String) str;
implements clause: class UnmodifiableList<T> implements
@Readonly List<@Readonly T> { ... } Throws exception déclaration:
void monitorTemperature() throws @Critical TemperatureException { ... }
Si une déclaration possède plusieurs annotations de même type, on parle d’annotation répétitive
apparue avec Java 8 (Repeating Annotation)
Exemple :
@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }
La définition d’une annotation reste inchangée :
@interface MyAnnotation {
String name () ;
Int va lue () default 0 ; String [] names() ;
… }
Les Annotations Prédéfinies :
Outre les annotations bien connues : @Deprecated (Java 5), @Override (Java 5),
@SuppressWarnings (Java 5) indique au compilateur de ne pas générer de warnings,
@SafeVarargs (Java 7) assume que le code dans la méthode/constructeur n’effectue pas des
opérations dangereuses sur ses paramètres de type tableau (varargs). Les warning sur ces paramètres
là sont toujours activés et ne peuvent être unchecked.
@FunctionalInterface (Java 8) indique que le type déclaré est supposé être un functional interface,
Les Annotations qui s’appliquent à d’autres annotations (META-ANNOTATION).
@Inherited (Java 5) – s’applique uniquement aux classes. Indique que l’annotation utilisée sur une
superclasse est héritée dans les sous-classes.
public class MySubClass extends MySuperClass {...}
MySubClass hérite de l’annotation @MyAnnotation
MySubClass.class.isAnnotationPresent(EntityAnnotation.cla
ss) retourne true !
java.lang.annotation.Inherit
ed
@Inherited
public @interface
MyAnnotation {
}
@MyAnnotation
public class MySuperClass {
... }
@Retention – Spécifie comment une annotation est utilisée:
RetentionPolicy.SOURCE – L’annotation est utilisée uniquement au niveau du code source et est ignoré par le compilateur.
RetentionPolicy.CLASS – L’annotation est utilisée uniquement par le compilateur à la compilation, mais elle est ignorée par la JVM.
RetentionPolicy.RUNTIME – L’annotation est utilisée uniquement par la JVM et peut donc être utilisée à l’exécution.
@Target (Java 5) – Permet de restreindre une annotation sur les types Java auxquels cette annotation
doit s’appliquer.
ElementType.ANNOTATION_TYPE peut être appliquée à une autre annotation.
ElementType.CONSTRUCTOR peut être appliquée à un constructeur.
ElementType.FIELD peut être appliquée à un attribut ou propriété.
ElementType.LOCAL_VARIABLE peut être appliquée à une locale variable.
ElementType.METHOD peut être appliquée à une méthode.
ElementType.PACKAGE peut être appliquée à une déclaration de package.
ElementType.PARAMETER peut être appliquée aux paramètres d’une méthode.
ElementType.TYPE peut être appliquée à tout élément d’une classe.
@Documented (Java 5) – Est utilisé pour indiquer à la javadoc que mes propres annotations doivent
être visibles sur les classes annotées avec.
@MyAnnotation
public class MySuperClass { ... }
java.lang.annotation.Documented
@Documented
public @interface MyAnnotation {
}
L’annotation @MyAnnotation est maintenant visible dans la javadoc de la classe MySuperClass.
@Repeatable (Java 8) – Indique qu’une annotation peut se répéter plusieurs fois sur une déclaration ou
un type.
4. Repeating Annotations (@see) –
Parfois il peut s’avérer utile de porter la même annotation plus d’une fois :
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }
Pour créer une annotation répétitive il faut la marquer avec la nouvelle méta-annotation
@Repeatable apparue avec Java 8.
import java.lang.annotation.Repeatable;
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour () default 12;
}
La valeur, entre parenthèses, de l’annotation @Repeatable correspond à une annotation conteneur qui
contiendra la liste des annotations répétitives.
Cette annotation conteur doit posséder un attribut value dont le type est un tableau d’éléments de type
l’annotation Repeatable qu’on est en train de créer.
public @interface Schedules {
Schedule [] value ();
}
5. Method Parameter Reflection –