Автор оригинала: 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 MutablePairmutablePair; @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 ImmutablePairimmutablePair = 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 .