Вступ до Apache Commons Lang 3

1. Огляд

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

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

У цьому підручнику ми глибоко заглибимось у найкорисніші класи бібліотеки .

2. Залежність Мавена

Як зазвичай, для початку використання Apache Commons Lang 3 нам спочатку потрібно додати залежність Maven:

 org.apache.commons commons-lang3 3.8 

3. Клас StringUtils

Першим класом корисності, який ми розглянемо в цьому вступному описі, є StringUtils.

Як випливає з назви, StringUtils дозволяє нам виконувати безліч нульово безпечних операцій s tring, які доповнюють / розширюють ті, що надаються 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 () , який повертає рядкове представлення даного масиву та конкретний рядок, коли масив має значення null:

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

Крім того, існують реалізації порівняння (), які працюють над байтом і коротким , які працюють дуже схоже на наведені вище приклади.

Далі в цьому огляді йдуть методи 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. Фракція Клас

Працювати з дробами все добре і добре, коли ми використовуємо ручку і аркуш паперу. Але чи нам доводиться проходити через складність цього процесу під час написання коду? Не зовсім.

Клас дробу робить додавання, віднімання і множення дробів на вітрі :

@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 { @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 () , надаючи конструктору різні параметри на основі типового вільного 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

Reflection is a first-class citizen in Apache Commons Lang 3.

The library includes several reflection classes, which allows us to reflectively access and manipulate class fields and methods.

For example, let's say that we've implemented a naive User domain class:

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

Assuming that its parameterized constructor is public, we can easily access it with the ConstructorUtils class:

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

Alternatively to standard class instantiation via constructors, we can reflectively create User instances by just calling the invokeConstructor() and invokeExactConstructor() methods:

@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. The FieldUtils Class

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 .

У бібліотеці реалізовано багато інших корисних класів, на які варто звернути увагу . Тут ми щойно продемонстрували найбільш корисні з них, базуючись на досить упевненому критерії.

Щоб отримати повний API бібліотеки, перевірте офіційний Javadocs.

Як завжди, усі зразки коду, показані в цьому посібнику, доступні на GitHub.