Une introduction à Apache Commons Lang 3

Une introduction à Apache Commons Lang 3

1. Vue d'ensemble

The Apache Commons Lang 3 libraryis a popular, full-featured package of utility classes, aimed at extending the functionality of the Java API.

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, telles que des paires et des triplets (généralement appeléstuples).

Dans ce didacticiel,we’ll take a deep dive at the library’s most useful utility classes.

2. La dépendance de Maven

Comme d'habitude, pour commencer à utiliser Apache Commons Lang 3, nous devons d'abord ajouter lesMaven dependency:


    org.apache.commons
    commons-lang3
    3.8

3. La classeStringUtils

La première classe d'utilité que nous aborderons dans ce tour d'horizon estStringUtils.

Comme son nom l'indique,StringUtils allows us to perform a bunch of null-safe strings operations that complement/extend the ones that java.lang.String provides out of the box.

Commençons par présenter l'ensemble des méthodes utilitaires qui effectuent plusieurs vérifications sur unstring donné, comme déterminer si lestring 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 classeStringUtils implémente de nombreuses autres méthodes, que nous avons omises ici par souci de simplicité.

Pour d'autres méthodes supplémentaires qui vérifient ou appliquent un type d'algorithme de conversion à unstring donné, veuillezcheck this tutorial.

Ceux que nous avons abordés ci-dessus sont vraiment simples, les tests unitaires doivent donc être explicites.

4. La classeArrayUtils

The ArrayUtils class implements a batch of utility methods that allow us to process and check arrays in many different shapes and forms.

Commençons par les deux implémentations surchargées de la méthodethe toString(), qui retourne une représentationstring duarray donné et unstring spécifique lorsque learray 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éthodeshasCode() ettoMap().

Le premier génère une implémentation personnalisée dehashCode pour unarray, tandis que le second convertit unarray en unMap:

@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éthodesisSameLength() etindexOf().

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 pour la classeStringUtils,ArrayUtils implémente beaucoup plus de méthodes supplémentaires. Vous pouvez en savoir plus à leur sujet dansthis tutorial.

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

5. La classeNumberUtils

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

Comme prévu,the class provides an extensive number of utility methods, aimed at processing and manipulating numeric types.

Regardons les implémentations surchargées de la méthodecompare(), qui compare l'égalité de différentes primitives, telles queint etlong:

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

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

De plus, il existe des implémentations decompare() qui fonctionnent surbyte etshort, qui fonctionnent de manière très similaire aux exemples ci-dessus.

Viennent ensuite les méthodescreateNumber() etisDigit().

Le premier permet de créer une représentation numérique d'unstring, tandis que le second vérifie si unstring est uniquement composé 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 classeNumberUtils fournit un support solide pour ces opérations via les implémentations surchargées des méthodesmin() etmax():

@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. LesFraction Class

Travailler avec des fractions, c'est très bien quand on utilise un stylo et un morceau de papier. Mais, devons-nous traverser les complexités de ce processus lors de l'écriture de code? Pas vraiment.

The Fraction class makes adding, subtracting and multiplying fractions in a breeze:

@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 classeFraction fournit un support précieux pour effectuer ces opérations de manière simple.

7. La classeSystemUtils

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 provides the SystemUtils class for accomplishing this in a painless way.

Considérons, par exemple, les méthodesgetJavaHome(),getUserHome() etisJavaVersionAtLeast():

@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 que la classeSystemUtils implémente. Nous les avons omis pour garder les exemples courts.

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

One of Apache Commons Lang 3’s most appealing facet is the implementation of some well-known design patterns, including the lazy-initialization and builder patterns.

Par exemple, disons que nous avons créé une classeUser 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 avons à faire est d'étendre la classe abstraite paramétréeLazyInitializer et de remplacer sa méthodeinitialize():

public class UserInitializer extends LazyInitializer {

    @Override
    protected User initialize() {
        return new User("John", "[email protected]");
    }
}

Maintenant, si nous voulons obtenir notre coûteux objetUser lorsque cela est nécessaire, nous appelons simplement la méthodeUserInitializer’s get():

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

The get() method is an implementation of the double-check idiom (thread-safe) for an instance field, as specified inJoshua Bloch’s “Effective Java”, item 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 classeHashCodeBuilder, qui nous permet de générer des implémentations dehashCode() 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 classeBasicThreadFactory, et créer des threads démons 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 classeConstructorUtils

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 et de manipuler de manière réfléchie les champs et les méthodes de la classe.

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

public class User {

    private String name;
    private String email;

    // standard constructors / getters / setters / toString
}

En supposant que son constructeur paramétré estpublic, nous pouvons facilement y accéder avec la classeConstructorUtils:

@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 instancesUser en appelant simplement les méthodesinvokeConstructor() etinvokeExactConstructor():

@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 classeFieldUtils

De même,we can use the methods of the FieldUtils class for reflectively reading/writing class fields.

Supposons que nous voulions obtenir un champ de la classeUser, ou éventuellement un champ que la classe hérite d'une superclasse.

Dans un tel cas, nous pouvons invoquer la méthodegetField():

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

Alternativement,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, nous utiliserons simplement la méthodegetDeclaredField():

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

De plus, nous pouvons utiliser la méthodegetAllFields() pour obtenir le nombre de champs de la classe reflétée, et écrire une valeur dans un champ déclaré ou un champ défini dans une hiérarchie avec leswriteField() etwriteDeclaredField() méthodes:

@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. La classeMethodUtils

Dans le même ordre d'idées, nous pouvons utiliser la réflexion sur les méthodes de classe avec la classeMethodUtils.

Dans ce cas, la visibilité de la méthodegetName() de la classeUser estpublic. Ainsi, nous pouvons y accéder avec la méthodegetAccessibleMethod():

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

Lorsqu'il s'agit d'appeler des méthodes de manière réfléchie, nous pouvons utiliser les méthodesinvokeExactMethod() etinvokeMethod():

@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. La classeMutableObject

While immutability is a key feature of good object-oriented software that we should default to in every possible case, malheureusement, nous devons parfois traiter des objets mutables.

De plus, la création de classes mutables nécessite beaucoup de code standard, qui peut être généré par la plupart des IDE via des traceurs générés automatiquement.

À cette fin, Apache Commons Lang 3 fournit la classeMutableObject, une classe wrapper simple pour créer des objets mutables avec un minimum de tracas:

@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");
}

Bien sûr, ce n'est qu'un exemple d'utilisation de la classeMutableObject.

En règle générale,we should always strive to create immutable classes, or in the worst case, provide only the required level of mutability.

13. La classeMutablePair

Curieusement, Apache Commons Lang 3 fournit un support puissant pour les n-uplets, sous forme de paires et de triples.

Supposons donc que nous devions créer une paire mutable d’éléments ordonnés.

Dans un tel cas, nous utiliserons la classeMutablePair:

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");
}

Le détail le plus pertinent à souligner ici est l’API propre de la classe.

Il nous permet de définir et d’accéder aux objets gauche et droit enveloppés par la paire via les setters / getters standard.

14. La classeImmutablePair

Sans surprise, il existe également une implémentation homologue immuable de la classeMutablePair, appeléeImmutablePair:

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");
}

Comme on peut s'y attendre d'une classe immuable, toute tentative de modifier l'état interne de la paire via la méthodesetValue() entraînera le déclenchement d'une exceptionUnsupportedOperationException.

15. The Triple Class

La dernière classe d'utilitaire à examiner ici estTriple.

Comme la classe est abstraite, nous pouvons créer des instancesTriple en utilisant la méthode de fabrique statiqueof():

@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");
}

Il existe également des implémentations concrètes pour les triplets mutables et immuables, via les classesMutableTriple etImmutableTriple.

Nous pouvons créer leurs instances via des constructeurs paramétrés, plutôt qu'avec une méthode de fabrique statique.

Dans ce cas, nous allons simplement les ignorer, car leurs API ressemblent beaucoup à celles des classesMutablePair etImmutablePair.

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 l'étagère *.

La bibliothèque implémente de nombreuses autres classes utilitaires qui valent la peine d'être examinées. Ici, nous venons de présenter les plus utiles, basées sur un critère assez avisé.

Pour l'API de bibliothèque complète, veuillez vérifier lesofficial Javadocs.

Comme d'habitude, tous les exemples de code présentés dans ce didacticiel sont disponibles surGitHub.