Une introduction à Apache Commons Lang 3

1. Vue d'ensemble

La bibliothèque Apache Commons Lang 3 est un package populaire et complet de classes d'utilitaires, visant à étendre les fonctionnalités de l'API Java .

Le répertoire de la bibliothèque est assez riche, allant de la manipulation de chaînes, de tableaux et de nombres, à la réflexion et à la concurrence, aux implémentations de plusieurs structures de données ordonnées, comme des paires et des triplets (généralement appelés tuples).

Dans ce didacticiel, nous allons approfondir les classes d'utilitaires les plus utiles de la bibliothèque .

2. La dépendance Maven

Comme d'habitude, pour commencer à utiliser Apache Commons Lang 3, nous devons d'abord ajouter la dépendance Maven:

 org.apache.commons commons-lang3 3.8 

3. La classe StringUtils

La première classe d'utilitaire que nous aborderons dans ce tour d' horizon est StringUtils.

Comme son nom l'indique, StringUtils nous permet d'effectuer un tas d' opérations de trings s sécurisées par zéro qui complètent / étendent celles que java.lang.String fournit prêtes à l'emploi .

Commençons par présenter l'ensemble des méthodes utilitaires qui effectuent plusieurs vérifications sur une chaîne donnée , comme déterminer si la chaîne est vide, vide, minuscule, majuscule, alphanumérique et ainsi de suite:

@Test public void whenCalledisBlank_thenCorrect() { assertThat(StringUtils.isBlank(" ")).isTrue(); } @Test public void whenCalledisEmpty_thenCorrect() { assertThat(StringUtils.isEmpty("")).isTrue(); } @Test public void whenCalledisAllLowerCase_thenCorrect() { assertThat(StringUtils.isAllLowerCase("abd")).isTrue(); } @Test public void whenCalledisAllUpperCase_thenCorrect() { assertThat(StringUtils.isAllUpperCase("ABC")).isTrue(); } @Test public void whenCalledisMixedCase_thenCorrect() { assertThat(StringUtils.isMixedCase("abC")).isTrue(); } @Test public void whenCalledisAlpha_thenCorrect() { assertThat(StringUtils.isAlpha("abc")).isTrue(); } @Test public void whenCalledisAlphanumeric_thenCorrect() { assertThat(StringUtils.isAlphanumeric("abc123")).isTrue(); } 

Bien sûr, la classe StringUtils implémente de nombreuses autres méthodes, que nous avons omises ici pour des raisons de simplicité.

Pour d'autres méthodes supplémentaires qui vérifient ou appliquent un type d'algorithme de conversion à une chaîne donnée , veuillez consulter ce didacticiel.

Celles que nous avons couvertes ci-dessus sont très simples, les tests unitaires doivent donc être explicites.

4. La classe ArrayUtils

La classe ArrayUtils implémente un lot de méthodes utilitaires qui nous permettent de traiter et de vérifier des tableaux dans de nombreuses formes et formes différentes .

Commençons par les deux implémentations surchargées de la méthode toString () , qui retourne une représentation sous forme de chaîne du tableau donné et une chaîne spécifique lorsque le tableau est nul:

@Test public void whenCalledtoString_thenCorrect() { String[] array = {"a", "b", "c"}; assertThat(ArrayUtils.toString(array)) .isEqualTo("{a,b,c}"); } @Test public void whenCalledtoStringIfArrayisNull_thenCorrect() { assertThat(ArrayUtils.toString(null, "Array is null")) .isEqualTo("Array is null"); } 

Ensuite, nous avons les méthodes hasCode () et toMap () .

Le premier génère une implémentation de hashCode personnalisée pour un tableau, tandis que le second convertit un tableau en Map :

@Test public void whenCalledhashCode_thenCorrect() { String[] array = {"a", "b", "c"}; assertThat(ArrayUtils.hashCode(array)) .isEqualTo(997619); } @Test public void whenCalledtoMap_thenCorrect() { String[][] array = {{"1", "one", }, {"2", "two", }, {"3", "three"}}; Map map = new HashMap(); map.put("1", "one"); map.put("2", "two"); map.put("3", "three"); assertThat(ArrayUtils.toMap(array)) .isEqualTo(map); }

Enfin, regardons les méthodes isSameLength () et indexOf () .

Le premier est utilisé pour vérifier si deux tableaux ont la même longueur, et le second pour obtenir l'index d'un élément donné:

@Test public void whenCalledisSameLength_thenCorrect() { int[] array1 = {1, 2, 3}; int[] array2 = {1, 2, 3}; assertThat(ArrayUtils.isSameLength(array1, array2)) .isTrue(); } @Test public void whenCalledIndexOf_thenCorrect() { int[] array = {1, 2, 3}; assertThat(ArrayUtils.indexOf(array, 1, 0)) .isEqualTo(0); } 

Comme avec la classe StringUtils , ArrayUtils implémente beaucoup plus de méthodes supplémentaires. Vous pouvez en apprendre plus à leur sujet dans ce didacticiel.

Dans ce cas, nous n'avons présenté que les plus représentatifs.

5. La classe NumberUtils

Un autre composant clé d'Apache Commons Lang 3 est la classe NumberUtils.

Comme prévu, la classe fournit un grand nombre de méthodes utilitaires destinées au traitement et à la manipulation de types numériques .

Regardons les implémentations surchargées de la méthode compare () , qui compare l'égalité de différentes primitives, telles que int et long :

@Test public void whenCalledcompareWithIntegers_thenCorrect() { assertThat(NumberUtils.compare(1, 1)) .isEqualTo(0); } @Test public void whenCalledcompareWithLongs_thenCorrect() { assertThat(NumberUtils.compare(1L, 1L)) .isEqualTo(0); }

En outre, il existe des implémentations de compare () qui fonctionnent sur byte et short , qui fonctionnent très similaires aux exemples ci-dessus.

Viennent ensuite les méthodes createNumber () et isDigit () .

Le premier permet de créer une représentation numérique d'une chaîne , tandis que le second vérifie si une chaîne est uniquement composée de chiffres:

@Test public void whenCalledcreateNumber_thenCorrect() { assertThat(NumberUtils.createNumber("123456")) .isEqualTo(123456); } @Test public void whenCalledisDigits_thenCorrect() { assertThat(NumberUtils.isDigits("123456")).isTrue(); } 

Lorsqu'il s'agit de trouver les valeurs mix et max d'un tableau fourni, la classe NumberUtils fournit un support solide pour ces opérations via les implémentations surchargées des méthodes min () et max () :

@Test public void whenCalledmaxwithIntegerArray_thenCorrect() { int[] array = {1, 2, 3, 4, 5, 6}; assertThat(NumberUtils.max(array)) .isEqualTo(6); } @Test public void whenCalledminwithIntegerArray_thenCorrect() { int[] array = {1, 2, 3, 4, 5, 6}; assertThat(NumberUtils.min(array)).isEqualTo(1); } @Test public void whenCalledminwithByteArray_thenCorrect() { byte[] array = {1, 2, 3, 4, 5, 6}; assertThat(NumberUtils.min(array)) .isEqualTo((byte) 1); }

6. La classe de fraction

Travailler avec des fractions est très bien lorsque nous utilisons un stylo et un morceau de papier. Mais devons-nous passer par les complexités de ce processus lors de l'écriture de code? Pas vraiment.

La classe Fraction permet d'ajouter, de soustraire et de multiplier des fractions en un rien de temps :

@Test public void whenCalledgetFraction_thenCorrect() { assertThat(Fraction.getFraction(5, 6)).isInstanceOf(Fraction.class); } @Test public void givenTwoFractionInstances_whenCalledadd_thenCorrect() { Fraction fraction1 = Fraction.getFraction(1, 4); Fraction fraction2 = Fraction.getFraction(3, 4); assertThat(fraction1.add(fraction2).toString()).isEqualTo("1/1"); } @Test public void givenTwoFractionInstances_whenCalledsubstract_thenCorrect() { Fraction fraction1 = Fraction.getFraction(3, 4); Fraction fraction2 = Fraction.getFraction(1, 4); assertThat(fraction1.subtract(fraction2).toString()).isEqualTo("1/2"); } @Test public void givenTwoFractionInstances_whenCalledmultiply_thenCorrect() { Fraction fraction1 = Fraction.getFraction(3, 4); Fraction fraction2 = Fraction.getFraction(1, 4); assertThat(fraction1.multiplyBy(fraction2).toString()).isEqualTo("3/16"); }

Bien que les opérations avec des fractions ne soient certainement pas la tâche la plus fréquente à laquelle nous devrons nous attaquer dans notre travail de développement quotidien, la classe Fraction fournit un support précieux pour effectuer ces opérations de manière simple.

7. La classe SystemUtils

Parfois, nous devons obtenir des informations dynamiques sur différentes propriétés et variables de la plate-forme Java sous-jacente ou du système d'exploitation.

Apache Commons Lang 3 fournit la classe SystemUtils pour accomplir cela de manière indolore .

Considérons, par exemple, les méthodes getJavaHome () , getUserHome () et isJavaVersionAtLeast () :

@Test public void whenCalledgetJavaHome_thenCorrect() { assertThat(SystemUtils.getJavaHome()) .isEqualTo(new File("path/to/java/jdk")); } @Test public void whenCalledgetUserHome_thenCorrect() { assertThat(SystemUtils.getUserHome()) .isEqualTo(new File("path/to/user/home")); } @Test public void whenCalledisJavaVersionAtLeast_thenCorrect() { assertThat(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_RECENT)).isTrue(); }

Il existe quelques méthodes utilitaires supplémentaires implémentées par la classe SystemUtils . Nous les avons omis pour garder les exemples courts.

8. Les classes d'initialisation paresseuse et de générateur

L'une des facettes les plus attrayantes d'Apache Commons Lang 3 est l'implémentation de certains modèles de conception bien connus, y compris les modèles d'initialisation paresseuse et de générateur .

Par exemple, disons que nous avons créé une classe User coûteuse (non illustrée par souci de concision), et que nous voulons différer son instanciation jusqu'à ce qu'elle soit vraiment nécessaire.

Dans un tel cas, tout ce que nous devons faire est d'étendre la classe abstraite paramétrée LazyInitializer et de remplacer sa méthode initialize () :

public class UserInitializer extends LazyInitializer { @Override protected User initialize() { return new User("John", "[email protected]"); } }

Maintenant, si nous voulons obtenir notre objet User coûteux lorsque cela est nécessaire, nous appelons simplement la méthode get () de UserInitializer :

@Test public void whenCalledget_thenCorrect() throws ConcurrentException { UserInitializer userInitializer = new UserInitializer(); assertThat(userInitializer.get()).isInstanceOf(User.class); }

La méthode get () est une implémentation de l'idiome de double vérification (thread-safe) pour un champ d'instance, comme spécifié dans «Effective Java» de Joshua Bloch, élément 71 :

private volatile User instance; User get() { if (instance == null) { synchronized(this) { if (instance == null) instance = new User("John", "[email protected]"); } } } return instance; }

De plus, Apache Commons Lang 3 implémente la classe HashCodeBuilder, qui nous permet de générer des implémentations de hashCode () en fournissant au constructeur différents paramètres, basés sur une API fluide typique:

@Test public void whenCalledtoHashCode_thenCorrect() { int hashcode = new HashCodeBuilder(17, 37) .append("John") .append("[email protected]") .toHashCode(); assertThat(hashcode).isEqualTo(1269178828); }

Nous pouvons faire quelque chose de similaire avec la classe BasicThreadFactory et créer des threads de démon avec un modèle de dénomination et une priorité:

@Test public void whenCalledBuilder_thenCorrect() { BasicThreadFactory factory = new BasicThreadFactory.Builder() .namingPattern("workerthread-%d") .daemon(true) .priority(Thread.MAX_PRIORITY) .build(); assertThat(factory).isInstanceOf(BasicThreadFactory.class); }

9. La classe ConstructorUtils

Reflection est un citoyen de premier ordre dans Apache Commons Lang 3.

La bibliothèque comprend plusieurs classes de réflexion, ce qui nous permet d'accéder de manière réfléchie et de manipuler les champs et méthodes de classe.

Par exemple, disons que nous avons implémenté une classe de domaine utilisateur naïve :

public class User { private String name; private String email; // standard constructors / getters / setters / toString }

En supposant que son constructeur paramétré est public , nous pouvons facilement y accéder avec la classe ConstructorUtils :

@Test public void whenCalledgetAccessibleConstructor_thenCorrect() { assertThat(ConstructorUtils .getAccessibleConstructor(User.class, String.class, String.class)) .isInstanceOf(Constructor.class); } 

Alternativement à l'instanciation de classe standard via des constructeurs, nous pouvons créer de manière réfléchie des instances User en appelant simplement les méthodes invokeConstructor () et invokeExactConstructor () :

@Test public void whenCalledinvokeConstructor_thenCorrect() throws Exception { assertThat(ConstructorUtils.invokeConstructor(User.class, "name", "email")) .isInstanceOf(User.class); } @Test public void whenCalledinvokeExactConstructor_thenCorrect() throws Exception { String[] args = {"name", "email"}; Class[] parameterTypes= {String.class, String.class}; assertThat(ConstructorUtils.invokeExactConstructor(User.class, args, parameterTypes)) .isInstanceOf(User.class); } 

10. La classe FieldUtils

Similarly, we can use the methods of the FieldUtils class for reflectively reading/writing class fields.

Let's suppose that we want to get a field of the User class, or eventually a field that the class is inheriting from a superclass.

In such a case, we can invoke the getField() method:

@Test public void whenCalledgetField_thenCorrect() { assertThat(FieldUtils.getField(User.class, "name", true).getName()) .isEqualTo("name"); } 

Alternatively, if we'd want to use a more restrictive reflection scope, and only get a field declared in the User class, and not inherited from a superclass, we'd just use the getDeclaredField() method:

@Test public void whenCalledgetDeclaredFieldForceAccess_thenCorrect() { assertThat(FieldUtils.getDeclaredField(User.class, "name", true).getName()) .isEqualTo("name"); }

In addition, we can use the getAllFields() method for getting the number of fields of the reflected class, and write a value to a declared field or a field defined up in a hierarchy with the writeField() and writeDeclaredField() methods:

@Test public void whenCalledgetAllFields_thenCorrect() { assertThat(FieldUtils.getAllFields(User.class).length) .isEqualTo(2); } @Test public void whenCalledwriteField_thenCorrect() throws IllegalAccessException { FieldUtils.writeField(user, "name", "Julie", true); assertThat(FieldUtils.readField(user, "name", true)) .isEqualTo("Julie"); } @Test public void givenFieldUtilsClass_whenCalledwriteDeclaredField_thenCorrect() throws IllegalAccessException { FieldUtils.writeDeclaredField(user, "name", "Julie", true); assertThat(FieldUtils.readField(user, "name", true)) .isEqualTo("Julie"); }

11. The MethodUtils Class

Along the same lines, we can use reflection on class methods with the MethodUtils class.

In this case, the visibility of the User class' getName() method is public. So, we can access it with the getAccessibleMethod() method:

@Test public void whenCalledgetAccessibleMethod_thenCorrect() { assertThat(MethodUtils.getAccessibleMethod(User.class, "getName")) .isInstanceOf(Method.class); } 

When it comes to reflectively invoking methods, we can use the invokeExactMethod() and invokeMethod() methods:

@Test public void whenCalledinvokeExactMethod_thenCorrect() throws Exception { assertThat(MethodUtils.invokeExactMethod(new User("John", "[email protected]"), "getName")) .isEqualTo("John"); } @Test public void whenCalledinvokeMethod_thenCorrect() throws Exception { User user = new User("John", "[email protected]"); Object method = MethodUtils.invokeMethod(user, true, "setName", "John"); assertThat(user.getName()).isEqualTo("John"); }

12. The MutableObject Class

While immutability is a key feature of good object-oriented software that we should default to in every possible case, unfortunately sometimes we need to deal with mutable objects.

Moreover, creating mutable classes requires a lot of boilerplate code, which can be generated by most IDEs through auto-generated setters.

To this end, Apache Commons Lang 3 provides the MutableObject class, a simple wrapper class for creating mutable objects with minimal fuss:

@BeforeClass public static void setUpMutableObject() { mutableObject = new MutableObject("Initial value"); } @Test public void whenCalledgetValue_thenCorrect() { assertThat(mutableObject.getValue()).isInstanceOf(String.class); } @Test public void whenCalledsetValue_thenCorrect() { mutableObject.setValue("Another value"); assertThat(mutableObject.getValue()).isEqualTo("Another value"); } @Test public void whenCalledtoString_thenCorrect() { assertThat(mutableObject.toString()).isEqualTo("Another value"); } 

Of course, this is just an example of how to use the MutableObject class.

As rule of thumb, we should always strive to create immutable classes, or in the worst case, provide only the required level of mutability.

13. The MutablePair Class

Interestingly enough, Apache Commons Lang 3 provides strong support for tuples, in the form of pairs and triples.

So, let's suppose that we need to create a mutable pair of ordered elements.

In such a case, we'd use the MutablePair class:

private static MutablePair mutablePair; @BeforeClass public static void setUpMutablePairInstance() { mutablePair = new MutablePair("leftElement", "rightElement"); } @Test public void whenCalledgetLeft_thenCorrect() { assertThat(mutablePair.getLeft()).isEqualTo("leftElement"); } @Test public void whenCalledgetRight_thenCorrect() { assertThat(mutablePair.getRight()).isEqualTo("rightElement"); } @Test public void whenCalledsetLeft_thenCorrect() { mutablePair.setLeft("newLeftElement"); assertThat(mutablePair.getLeft()).isEqualTo("newLeftElement"); } 

The most relevant detail worth stressing here is the class' clean API.

It allows us to set and access the left and right objects wrapped by the pair through the standard setters/getters.

14. The ImmutablePair Class

Unsurprisingly, there's also an immutable counterpart implementation of the MutablePair class, called ImmutablePair:

private static ImmutablePair immutablePair = new ImmutablePair("leftElement", "rightElement"); @Test public void whenCalledgetLeft_thenCorrect() { assertThat(immutablePair.getLeft()).isEqualTo("leftElement"); } @Test public void whenCalledgetRight_thenCorrect() { assertThat(immutablePair.getRight()).isEqualTo("rightElement"); } @Test public void whenCalledof_thenCorrect() { assertThat(ImmutablePair.of("leftElement", "rightElement")) .isInstanceOf(ImmutablePair.class); } @Test(expected = UnsupportedOperationException.class) public void whenCalledSetValue_thenThrowUnsupportedOperationException() { immutablePair.setValue("newValue"); } 

As we might expect from an immutable class, any attempt to change the pair's internal state through the setValue() method will result in throwing an UnsupportedOperationException exception.

15. The Triple Class

The last utility class that will look at here is Triple.

As the class is abstract, we can create Triple instances by using the of() static factory method:

@BeforeClass public static void setUpTripleInstance() { triple = Triple.of("leftElement", "middleElement", "rightElement"); } @Test public void whenCalledgetLeft_thenCorrect() { assertThat(triple.getLeft()).isEqualTo("leftElement"); } @Test public void whenCalledgetMiddle_thenCorrect() { assertThat(triple.getMiddle()).isEqualTo("middleElement"); } @Test public void whenCalledgetRight_thenCorrect() { assertThat(triple.getRight()).isEqualTo("rightElement"); }

There are also concrete implementations for both mutable and immutable triples, through the MutableTriple and ImmutableTriple classes.

We can create their instances via parameterized constructors, rather than with a static factory method.

In this case, we'll just skip them, as their APIs look very similar to the ones of the MutablePair and ImmutablePair classes.

16. Conclusion

In this tutorial, we took an in-depth look at some of the most useful utility classes that Apache Commons Lang 3 provides off the shelf .

La bibliothèque implémente de nombreuses autres classes utilitaires qui méritent d'être examinées . Ici, nous venons de présenter les plus utiles, sur la base d'un critère assez opiniâtre.

Pour consulter l'API de bibliothèque complète, veuillez consulter les Javadocs officiels.

Comme d'habitude, tous les exemples de code présentés dans ce didacticiel sont disponibles à l'adresse over sur GitHub.