1. Обзор
Библиотека Apache Commons Lang 3 — это популярный полнофункциональный пакет служебных классов, направленный на расширение функциональности Java API .
Репертуар библиотеки довольно богат: от обработки строк, массивов и чисел, отражения и параллелизма до реализации нескольких упорядоченных структур данных, таких как пары и тройки (известные как кортежи ).
В этом уроке мы подробно рассмотрим наиболее полезные служебные классы библиотеки .
2. Зависимость от Maven
Как обычно, чтобы начать использовать Apache Commons Lang 3, нам сначала нужно добавить зависимость Maven :
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
3. Класс StringUtils
Первый служебный класс, который мы рассмотрим в этом вводном обзоре, — StringUtils .
Как следует из названия, StringUtils
позволяет нам выполнять ряд операций со строками, безопасных для нулей
, которые дополняют/расширяют те, которые предоставляет java.lang.String
из коробки .
Давайте начнем демонстрировать набор служебных методов, которые выполняют несколько проверок заданной строки
, например определяют, является ли строка
пустой, пустой, строчной, прописной, буквенно-цифровой и т. д.:
@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");
}
Далее у нас есть методы hasCode()
и toMap()
.
Первый генерирует пользовательскую реализацию hashCode для массива,
а второй преобразует массив
в 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);
}
Наконец, давайте рассмотрим методы isSameLength()
и 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
и работают очень похоже на приведенные выше примеры.
Следующими в этом обзоре являются методы createNumber()
и isDigit()
.
Первый позволяет нам создать числовое представление строки
, а второй проверяет, состоит ли строка
только из цифр:
@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 Lang 3 является реализация некоторых хорошо известных шаблонов проектирования, включая шаблоны отложенной инициализации и построения .
Например, предположим, что мы создали дорогостоящий класс User
(не показан для краткости) и хотим отложить его создание до тех пор, пока он действительно не понадобится.
В таком случае все, что нам нужно сделать, это расширить параметризованный абстрактный класс LazyInitializer и переопределить его метод initialize() :
public class UserInitializer extends LazyInitializer<User> {
@Override
protected User initialize() {
return new User("John", "john@domain.com");
}
}
Теперь, если мы хотим получить наш дорогостоящий объект 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", "john@domain.com");
}
}
}
return instance;
}
Кроме того, Apache Commons Lang 3 реализует класс HashCodeBuilder , который позволяет нам генерировать реализации hashCode()
, предоставляя построителю различные параметры на основе типичного плавного API:
@Test
public void whenCalledtoHashCode_thenCorrect() {
int hashcode = new HashCodeBuilder(17, 37)
.append("John")
.append("john@domain.com")
.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
Reflection является первоклассным гражданином в Apache Commons Lang 3.
Библиотека включает в себя несколько классов отражения, что позволяет нам получать доступ к полям и методам классов и манипулировать ими.
Например, предположим, что мы реализовали наивный класс домена пользователя :
public class User {
private String name;
private String email;
// standard constructors / getters / setters / toString
}
Предполагая, что его параметризованный конструктор является общедоступным
, мы можем легко получить к нему доступ с помощью класса 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
.
В этом случае видимость метода getName()
класса User
является общедоступной
. Итак, мы можем получить к нему доступ с помощью метода 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", "john@domain.com"), "getName"))
.isEqualTo("John");
}
@Test
public void whenCalledinvokeMethod_thenCorrect()
throws Exception {
User user = new User("John", "john@domain.com");
Object method = MethodUtils.invokeMethod(user, true, "setName", "John");
assertThat(user.getName()).isEqualTo("John");
}
12. Класс изменяемого объекта
Хотя неизменяемость является ключевой особенностью хорошего объектно-ориентированного программного обеспечения, которую мы должны использовать по умолчанию в каждом возможном случае , к сожалению, иногда нам приходится иметь дело с изменяемыми объектами.
Кроме того, для создания изменяемых классов требуется много шаблонного кода, который может быть сгенерирован большинством IDE с помощью автоматически генерируемых сеттеров.
С этой целью Apache Commons Lang 3 предоставляет класс 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");
}
Конечно, это всего лишь пример использования класса MutableObject
.
Как правило, мы всегда должны стремиться создавать неизменяемые классы или, в худшем случае, обеспечивать только необходимый уровень изменчивости .
13. Класс MutablePair
Интересно, что Apache Commons Lang 3 обеспечивает мощную поддержку кортежей в виде пар и троек.
Итак, давайте предположим, что нам нужно создать изменяемую пару упорядоченных элементов.
В таком случае мы бы использовали класс MutablePair
:
private static MutablePair<String, String> 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<String, String> 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() :
@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 Lang 3 предоставляет в готовом виде .
Библиотека реализует множество других служебных классов, на которые стоит обратить внимание . Здесь мы только что продемонстрировали самые полезные из них, основанные на довольно самоуверенном критерии.
Полный API библиотеки см. в официальной документации Javadocs .
Как обычно, все примеры кода, показанные в этом руководстве, доступны на GitHub .