Рубрики
Без рубрики

Введение в Apache Commons Lang 3

Краткий и практический обзор библиотеки Apache Commons Lang3.

Автор оригинала: Alejandro Ugarte.

1. Обзор

Библиотека Apache Commons Lang 3 -это популярный полнофункциональный пакет служебных классов, направленный на расширение функциональности Java API .

Репертуар библиотеки довольно богат, начиная от обработки строк, массивов и чисел, отражения и параллелизма, до реализации нескольких упорядоченных структур данных, таких как пары и тройки (обычно известные как кортежи ).

В этом уроке мы глубоко погрузимся в самые полезные классы утилит библиотеки .

2. Зависимость Maven

Как обычно, чтобы начать использовать Apache Commons Lang3, нам сначала нужно добавить зависимость Maven :


    org.apache.commons
    commons-lang3
    3.11

3. Класс StringUtils

Первый класс утилит, который мы рассмотрим в этом вводном обзоре, – это StringUtils .

Как следует из его названия, StringUtils позволяет нам выполнять множество операций с нулевыми значениями string , которые дополняют/расширяют те, которые java.lang.Строка предоставляет из коробки .

Давайте начнем демонстрировать набор служебных методов , которые выполняют несколько проверок для данной строки , например, определяют, является ли строка пустой, пустой, строчной, прописной, буквенно-цифровой и так далее:

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

Конечно, класс StringUtils реализует множество других методов, которые мы опустили здесь для простоты.

Для некоторых других дополнительных методов , которые проверяют или применяют некоторый тип алгоритма преобразования к заданной строке , пожалуйста, проверьте этот учебник .

Те, которые мы рассмотрели выше, действительно просты, поэтому модульные тесты должны быть понятны сами по себе.

4. Класс ArrayUtils

Класс ArrayUtils реализует пакет служебных методов, которые позволяют нам обрабатывать и проверять массивы во многих различных формах и формах .

Давайте начнем с двух перегруженных реализаций метода toString () , который возвращает строку представление данного массива и конкретную строку , когда массив равен нулю:

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

Далее у нас есть методы hashcode() и to Map () .

Первый генерирует пользовательскую реализацию hashCode для массива, в то время как последний преобразует массив в карту :

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

Наконец, давайте рассмотрим методы is Same Length() и indexOf () .

Первый используется для проверки, имеют ли два массива одинаковую длину, а второй-для получения индекса данного элемента:

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

Как и в случае с классом StringUtils , ArrayUtils реализует гораздо больше дополнительных методов. Вы можете узнать больше о них в этом уроке .

В данном случае мы продемонстрировали только самые репрезентативные из них.

5. Класс NumberUtils

Еще одним ключевым компонентом Apache Commons Lang 3 является класс NumberUtils .

Как и ожидалось, класс предоставляет большое количество полезных методов, направленных на обработку и манипулирование числовыми типами .

Давайте рассмотрим перегруженные реализации метода compare () , который сравнивает равенство различных примитивов, таких как int и 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);
}

Кроме того, существуют реализации compare () , которые работают с byte и short , которые работают очень похоже на приведенные выше примеры.

Далее в этом обзоре приведены методы create Number() и isDigit () .

Первый позволяет нам создать числовое представление string , в то время как второй проверяет, состоит ли string только из цифр:

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

Когда дело доходит до поиска смешанных и максимальных значений поставляемого массива, класс NumberUtils обеспечивает сильную поддержку этих операций с помощью перегруженных реализаций методов min() и 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. Класс Фракций

Работа с дробями-это все прекрасно и хорошо, когда мы используем ручку и лист бумаги. Но должны ли мы проходить через сложности этого процесса при написании кода? Не совсем.

Класс Fraction делает сложение, вычитание и умножение дробей на одном дыхании :

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

Хотя операции с дробями, безусловно, не являются самой частой задачей, которую нам придется решать в нашей повседневной работе по разработке, класс Fraction обеспечивает ценную поддержку для выполнения этих операций простым способом.

7. Класс SystemUtils

Иногда нам нужно получить некоторую динамическую информацию о различных свойствах и переменных базовой платформы Java или операционной системы.

Apache Commons Lang 3 предоставляет SystemUtils класс для выполнения этого безболезненным способом .

Рассмотрим, например, методы getJavaHome() , getUserHome() и 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();
}

Существует несколько дополнительных служебных методов, которые реализует класс SystemUtils . Мы опустили их, чтобы сократить примеры.

8. Классы ленивой инициализации и компоновщика

Одним из наиболее привлекательных аспектов Apache Commons Lang3 является реализация некоторых хорошо известных шаблонов проектирования, включая шаблоны lazy-initialization и builder .

Например, предположим, что мы создали дорогой класс User (не показан для краткости) и хотим отложить его создание до тех пор, пока он действительно не понадобится.

В таком случае все, что нам нужно сделать, это расширить параметризованный LazyInitializer абстрактный класс и переопределить его initialize() метод:

public class UserInitializer extends LazyInitializer {

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

Теперь, если мы хотим получить ваш дорогостоящий объект User , когда это необходимо, мы просто вызываем метод get() UserInitializer/|:

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

Метод get() является реализацией идиомы двойной проверки (потокобезопасной) для поля экземпляра, как указано в “Эффективной Java” Джошуа Блоха, пункт 71 :

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

Кроме того, Apache Commons Lang 3 реализует класс HashCodeBuilder , который позволяет нам генерировать реализации hashCode () , предоставляя построителю различные параметры на основе типичного fluent API:

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

Мы можем сделать что-то подобное с классом BasicThreadFactory и создать потоки демонов с шаблоном именования и приоритетом:

@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. Класс ConstructorUtils

Отражение-первоклассный гражданин в Apache Commons Lang 3.

Библиотека включает в себя несколько классов отражения, что позволяет нам рефлексивно обращаться к полям и методам классов и манипулировать ими.

Например, предположим, что мы реализовали наивный класс User domain:

public class User {

    private String name;
    private String email;
    
    // standard constructors / getters / setters / toString
}

Предполагая , что его параметризованный конструктор является public , мы можем легко получить к нему доступ с помощью класса ConstructorUtils :

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

В качестве альтернативы стандартному созданию экземпляров классов с помощью конструкторов мы можем рефлексивно создавать экземпляры User , просто вызывая методы invokeConstructor() и 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. Класс FieldUtils

Аналогично, мы можем использовать методы класса FieldUtils для рефлексивного чтения/записи полей класса .

Предположим, что мы хотим получить поле класса User или, в конечном счете, поле, которое класс наследует от суперкласса.

В таком случае мы можем вызвать метод getField() :

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

В качестве альтернативы, если бы мы хотели использовать более ограничительную область отражения и получить только поле, объявленное в классе User , а не унаследованное от суперкласса , мы бы просто использовали метод getDeclaredField() :

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

Кроме того, мы можем использовать метод getAllFields() для получения количества полей отраженного класса и записать значение в объявленное поле или поле, определенное в иерархии с помощью методов writeField() и writeDeclaredField() :

@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. Класс MethodUtils

В том же духе мы можем использовать отражение методов класса с помощью класса MethodUtils .

В этом случае видимость метода User class’ getName() является public . Таким образом, мы можем получить к нему доступ с помощью метода getAccessibleMethod() :

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

Когда дело доходит до рефлексивного вызова методов, мы можем использовать методы invokeExactMethod() и invokeMethod() :

@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. Изменяемый класс Объектов

Хотя неизменяемость является ключевой особенностью хорошего объектно-ориентированного программного обеспечения , которое мы должны использовать по умолчанию во всех возможных случаях , к сожалению, иногда нам приходится иметь дело с изменяемыми объектами.

Кроме того, для создания изменяемых классов требуется много шаблонного кода, который может быть сгенерирован большинством IDE с помощью автоматически сгенерированных установщиков.

С этой целью Apache Commons Lang3 предоставляет класс MutableObject , простой класс-оболочку для создания изменяемых объектов с минимальной суетой:

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

Конечно, это всего лишь пример того, как использовать Изменяемый объект класс.

Как правило, мы всегда должны стремиться создавать неизменяемые классы или, в худшем случае, обеспечивать только необходимый уровень изменчивости .

13. Класс MutablePair

Интересно, что Apache Commons Lang3 обеспечивает сильную поддержку кортежей в виде пар и троек.

Итак, предположим, что нам нужно создать изменяемую пару упорядоченных элементов.

В таком случае мы бы использовали класс MutablePair :

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

Самая важная деталь, которую стоит подчеркнуть здесь, – это чистый API класса.

Это позволяет нам устанавливать и получать доступ к левым и правым объектам, обернутым парой, через стандартные сеттеры/геттеры.

14. Класс ImmutablePair

Неудивительно, что существует также неизменяемая параллельная реализация класса MutablePair , называемая 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");
}

Как и следовало ожидать от неизменяемого класса, любая попытка изменить внутреннее состояние пары с помощью метода setValue() приведет к созданию исключения UnsupportedOperationException|/.

15. Тройной Класс

Последний служебный класс, который мы рассмотрим здесь, – это Triple .

Поскольку класс является абстрактным, мы можем создать Triple экземпляры с помощью метода of() static factory:

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

Существуют также конкретные реализации как для изменяемых, так и для неизменяемых троек с помощью классов MutableTriple и ImmutableTriple .

Мы можем создавать их экземпляры с помощью параметризованных конструкторов, а не с помощью статического заводского метода.

В этом случае мы просто пропустим их, так как их API-интерфейсы очень похожи на интерфейсы классов MutablePair и ImmutablePair .

16. Заключение

В этом уроке мы подробно рассмотрели некоторые из наиболее полезных классов утилит, которые предоставляет Apache Commons Lang3 off the shelf .

Библиотека реализует множество других полезных классов, на которые стоит обратить внимание . Здесь мы только что продемонстрировали наиболее полезные из них, основанные на довольно самоуверенном критерии.

Для получения полного API библиотеки, пожалуйста, проверьте официальные Javadocs .

Как обычно, все примеры кода, показанные в этом руководстве, доступны на GitHub .