- Як підключити?
- Приклади з моєї роботи: як не знаючи про такий корисний клас, я створював свій
велосипедмабоця. - Розбираємо інші методи, які мені видалися цікавими.
- Підбиваємо підсумок.
0. Як підключити
Ті, хто йде зі мною нога в ногу, вже більш-менш знайомі і з гітом, і з мавеном, так що далі я спиратимуся на ці знання і не повторюватимусь. Тим же, хто пропустив мої попередні статті або тільки приєднався до читання — ось матеріали про мавен і гіт . Звичайно, без системи складання (мавен, гредл) можна також підключити все ручками, але це дико в наш час і так робити не потрібно: краще відразу вчитися робити все правильно. Тому для роботи з Мавен спочатку додаємо відповідну залежність:<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${apache.common.version}</version>
</dependency>
Де ${apache.common.version} — версія цієї бібліотеки. Далі, щоб імпортувати в якомусь класі, додаємо імпорт:
import org.apache.commons.lang3.StringUtils;
І все, справа в капелюсі))
1. Приклади з реального проекту
- leftPad метод
Перший приклад взагалі зараз здається настільки дурним, що дуже добре, що мої колеги знали про StringUtils.leftPad та підказали мені. Яке було завдання: код був побудований так, що потрібно було зробити трансформацію даних, якщо вони прийшли не зовсім коректно. Очікувалося, що рядкове поле має складатися лише із цифр, тобто. якщо його довжина його 3, а значення - 1, то запис має бути "001". Тобто спочатку потрібно видалити всі прогалини, а потім замостити вже це нулями. Ще прикладів, щоб було зрозуміло суть завдання: з “12” -> “012” з “1” -> “001” І таке інше. Що я зробив? Описав це у класі LeftPadExample . Я написав метод, який це все зробить:
public static String ownLeftPad(String value) {
String trimmedValue = value.trim();
if(trimmedValue.length() == value.length()) {
return value;
}
StringBuilder newValue = new StringBuilder(trimmedValue);
IntStream.rangeClosed(1, value.length() - trimmedValue.length())
.forEach(it -> newValue.insert(0, "0"));
return newValue.toString();
}
За основу взяв ідею, що ми можемо просто отримати різницю між оригінальним та обрізаним значенням та заповнити попереду нулями. Для цього я використовував IntStream , щоб n раз зробити одну й ту саму операцію. І це точно потрібно тестувати. А ось що можна було зробити, якби я заздалегідь знав про метод StringUtils.leftPad :
public static String apacheCommonLeftPad(String value) {
return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Як бачите, коду набагато менше, при цьому ще й використовується всіма підтверджена бібліотека. Для цієї справи я створив два тести в класі LeftPadExampleTest (зазвичай, коли планують тестувати якийсь клас, створюють у такому ж пакеті, тільки в src/test/java, клас з таким же ім'ям+Test). Ці тести перевіряють один метод, щоб він правильно трансформував значення, то інший. Звичайно, тестів потрібно написати набагато більше, але тема тестування в нашому випадку не головна:
package com.github.codegymcommunity.stringutilsdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("Unit-level testing for LeftPadExample")
class LeftPadExampleTest {
@DisplayName("Should transform by using ownLeftPad method as expected")
@Test
public void shouldTransformOwnLeftPadAsExpected() {
//given
String value = "1 ";
String expectedTransformedValue = "0001";
//when
String transformedValue = LeftPadExample.ownLeftPad(value);
//then
Assertions.assertEquals(expectedTransformedValue, transformedValue);
}
@DisplayName("Should transform by using StringUtils method as expected")
@Test
public void shouldTransformStringUtilsLeftPadAsExpected() {
//given
String value = "1 ";
String expectedTransformedValue = "0001";
//when
String transformedValue = LeftPadExample.apacheCommonLeftPad(value);
//then
Assertions.assertEquals(expectedTransformedValue, transformedValue);
}
}
Можу поки що зробити кілька коментарів щодо тестів. Написані вони по JUnit 5:
- Тест сприйматиметься як тест, якщо він має відповідну інструкцію — @Test.
- Якщо в імені складно описати роботу тесту або опис довгий і його незручно читати, можна додати анотацію @DisplayName і зробити в ній нормальний опис, який буде видно при запуску тестів.
- При написанні тестів я використовую підхід BDD, в якому розділяю тести на логічні частини:
- //given - блок налаштування даних перед тестом;
- //when - Блок, де запускається та частина коду, яку ми тестуємо;
- //then - Блок, в якому проходять перевірки результатів роботи блоку when.
- stripStart метод
Тут мені потрібно було вирішити питання з рядком, на початку якого могли бути прогалини та коми. Після трансформації їх мало бути у новому значенні. Постановка завдання зрозуміла як ніколи. Декілька прикладів закріпить наше розуміння: ", , books" -> "books" ",,,books" -> "books" b , books" -> "b , books" Як і для випадку з leftPad, додав клас StrimStartExample , в якому два методи. Один — із власним рішенням:
public static String ownStripStart(String value) {
int index = 0;
List commaSpace = asList(" ", ",");
for (int i = 0; i < value.length(); i++) {
if (commaSpace.contains(String.valueOf(value.charAt(i)))) {
index++;
} else {
break;
}
}
return value.substring(index);
}
Тут ідея полягала в тому, щоб знайти той індекс, починаючи з якого вже немає прогалин і ком. Якщо їх зовсім не було на початку, індекс буде нуль. І другий - з рішенням через StringUtils :
public static String apacheCommonLeftPad(String value) {
return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Тут ми передаємо першим аргументом інформацію про те, з яким рядком працюємо, а в другому передаємо рядок, що складається із символів, які потрібно пропустити. Так само створюємо StripStartExampleTest клас:
package com.github.codegymcommunity.stringutilsdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("Unit-level testing for StripStartExample")
class StripStartExampleTest {
@DisplayName("Should transform by using stripStart method as expected")
@Test
public void shouldTransformOwnStripStartAsExpected() {
//given
String value = ", , books";
String expectedTransformedValue = "books";
//when
String transformedValue = StripStartExample.ownStripStart(value);
//then
Assertions.assertEquals(expectedTransformedValue, transformedValue);
}
@DisplayName("Should transform by using StringUtils method as expected")
@Test
public void shouldTransformStringUtilsStripStartAsExpected() {
//given
String value = ", , books";
String expectedTransformedValue = "books";
//when
String transformedValue = StripStartExample.apacheCommonLeftPad(value);
//then
Assertions.assertEquals(expectedTransformedValue, transformedValue);
}
}
- isEmpty метод
Цей метод, звичайно, набагато простіший, але від цього він не менш корисний. Він розширює можливості методу String.isEmpty() , який ще додається перевірка на null. Навіщо? Щоб не було NullPointerException, тобто, щоб уникнути виклику методів змінної, яка є null . Тому щоб не писати:
if(value != null && value.isEmpty()) {
//doing something
}
Можна просто зробити так:
if(StringUtils.isEmpty(value)) {
//doing something
}
Плюс цього в тому, що відразу видно де який метод використовуються.
2. Розбір інших методів класу StringUtils
Тепер поговоримо про ті методи, які на мій погляд теж заслуговують на увагу. Говорячи загалом про StringUtils , варто сказати, що він надає null безпечні методи-аналоги тих, що є в класі String (як у випадку з методом isEmpty ). Пройдемося ними:
- compare метод
Такий метод є в String і буде NullPointerException, якщо в порівнянні двох рядків один з них буде null. Щоб уникнути потворних перевірок у нашому коді, можемо використовувати метод StringUtils.compare (String str1, String str2) : він повертає int як результат порівняння. Що означають ці значення? int = 0, якщо вони однакові (або обидва null). int < 0, if str1 менше, ніж str2. int > 0, якщо str1 більше, ніж str2. Також якщо подивитися на їхню документацію, то в Javadoc цього методу представлені наступні варіанти розвитку подій:
StringUtils.compare(null, null) = 0
StringUtils.compare(null , "a") < 0
StringUtils.compare("a", null) > 0
StringUtils.compare("abc", "abc") = 0
StringUtils.compare("a", "b") < 0
StringUtils.compare("b", "a") > 0
StringUtils.compare("a", "B") > 0
StringUtils.compare("ab", "abc") < 0
- contains... методи
Тут розробники утиліти розгулялися на славу. Який метод хочеш є. Я вирішив їх зібрати докупи:
-
contains - метод, що перевіряє, чи є передбачуваний рядок всередині іншого рядка. Чим це корисно? Можна використовувати цей метод, якщо потрібно переконатися, що є слово в тексті.
Приклади:
StringUtils.contains(null, *) = false StringUtils.contains(*, null) = false StringUtils.contains("", "") = true StringUtils.contains("abc", "") = true StringUtils.contains("abc", "a") = true StringUtils.contains("abc", "z") = false
Знов-таки, NPE (Null Pointer Exception) безпека є.
containsAny — метод, що перевіряє, чи є хоч якийсь символ із представлених у рядку. Також корисна річ: часто доводиться таке виконувати. Приклади документації:
StringUtils.containsAny(null, *) = false StringUtils.containsAny("", *) = false StringUtils.containsAny(*, null) = false StringUtils.containsAny(*, []) = false StringUtils.containsAny("zzabyycdxx", ['z', 'a']) = true StringUtils.containsAny("zzabyycdxx", ['b', 'y']) = true StringUtils.containsAny("zzabyycdxx", ['z', 'y']) = true StringUtils.containsAny("aba", ['z']) = false
-
containsIgnoreCase - корисне розширення для методу contains . Справді, щоб перевірити такий випадок без цього, доведеться перебрати кілька варіантів. А так гармонійно буде використано лише один метод.
-
containsNone - вже судячи з назви можна зрозуміти, що перевіряється. Рядок усередині не повинно бути. Корисна річ, безумовно. Швидкий пошук якихось неугодних символів;). У нашому телеграм-боті фільтруватимемо мати, не пройдемо повз ці кумедні методи.
І приклади, куди ж без них:
StringUtils.containsNone(null, *) = true StringUtils.containsNone(*, null) = true StringUtils.containsNone("", *) = true StringUtils.containsNone("ab", '') = true StringUtils.containsNone("abab", 'xyz') = true StringUtils.containsNone("ab1", 'xyz') = true StringUtils.containsNone("abz", 'xyz') = false
Декілька прикладів з доки:
StringUtils.containsIgnoreCase(null, *) = false
StringUtils.containsIgnoreCase(*, null) = false
StringUtils.containsIgnoreCase("", "") = true
StringUtils.containsIgnoreCase("abc", "") = true
StringUtils.containsIgnoreCase("abc", "a") = true
StringUtils.containsIgnoreCase("abc", "z") = false
StringUtils.containsIgnoreCase("abc", "A") = true
StringUtils.containsIgnoreCase("abc", "Z") = false
- defaultString метод
Серія методів, які допомагають уникнути додавання зайвого іфчика у випадку, якщо рядок null і потрібно поставити якесь значення за промовчанням. Варіантів є багато на будь-який смак. Головний серед них - StringUtils.defaultString(final String str, final String defaultStr) - у випадку, якщо str дорівнює null, ми просто передамо значення defaultStr . Приклади документації:
StringUtils.defaultString(null, "NULL") = "NULL"
StringUtils.defaultString("", "NULL") = ""
StringUtils.defaultString("bat", "NULL") = "bat"
Його дуже зручно використовувати, коли створюєш клас POJO з даними.
- deleteWhitespace метод
Це цікавий метод, хоч і варіантів його застосування не так уже й багато. Разом з тим, якщо такий випадок представиться, метод точно буде дуже корисним. Він видаляє всі прогалини з рядка. Де б цей пробіл не був, від нього не залишиться і сліду))) Приклади з доки:
StringUtils.deleteWhitespace(null) = null
StringUtils.deleteWhitespace("") = ""
StringUtils.deleteWhitespace("abc") = "abc"
StringUtils.deleteWhitespace(" ab c ") = "abc"
- endsWith метод
Говорить сам за себе. Це дуже корисний метод: він перевіряє, чи закінчується рядок пропонованим рядком чи ні. Часто таке таке потрібне. Звичайно, можна написати перевірку і самому, але використовувати вже готовий метод явно зручніше і краще. Приклади:
StringUtils.endsWith(null, null) = true
StringUtils.endsWith(null, "def") = false
StringUtils.endsWith("abcdef", null) = false
StringUtils.endsWith("abcdef", "def") = true
StringUtils.endsWith("ABCDEF", "def") = false
StringUtils.endsWith("ABCDEF", "cde") = false
StringUtils.endsWith("ABCDEF", "") = true
Як бачимо, все закінчується на порожній рядок))) Думаю, що цей приклад (StringUtils.endsWith("ABCDEF", "") = true) просто йде як бонус, адже це абсурд) Також є там і метод, який ігнорує регістр .
- equals метод
Відмінний приклад null безпечного методу, який порівнює два рядки. Що б ми туди не поклали, відповідь буде і буде без помилок. Приклади:
StringUtils.equals(null, null) = true
StringUtils.equals(null, "abc") = false
StringUtils.equals("abc", null) = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false
Зрозуміло, є і equalsIgnoreCase - виконується так само, тільки ігноруємо регістр. Подивимося?
StringUtils.equalsIgnoreCase(null, null) = true
StringUtils.equalsIgnoreCase(null, "abc") = false
StringUtils.equalsIgnoreCase("abc", null) = false
StringUtils.equalsIgnoreCase("abc", "abc") = true
StringUtils.equalsIgnoreCase("abc", "ABC") = true
- equalsAny метод
Ідемо далі і розширюємо метод equals . Допустимо, замість кількох перевірок на рівність, ми хочемо виконати одну. Ось для цього ми можемо передати рядок, з яким порівнюватимуть і набір рядків, якщо якийсь із них дорівнюватиме запропонованому — то буде TRUE. Передаємо рядок та колекцію рядків, щоб порівняти їх між собою (перший рядок із рядками з колекції). Важко? Ось приклади з доки, які допоможуть зрозуміти, що мають на увазі:
StringUtils.equalsAny(null, (CharSequence[]) null) = false
StringUtils.equalsAny(null, null, null) = true
StringUtils.equalsAny(null, "abc", "def") = false
StringUtils.equalsAny("abc", null, "def") = false
StringUtils.equalsAny("abc", "abc", "def") = true
StringUtils.equalsAny("abc", "ABC", "DEF") = false
Також є equalsAnyIgnoreCase . І приклади для нього:
StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
StringUtils.equalsAnyIgnoreCase(null, null, null) = true
StringUtils.equalsAnyIgnoreCase(null, "abc", "def") = false
StringUtils.equalsAnyIgnoreCase("abc", null, "def") = false
StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true
Підсумок
У результаті ми йдемо зі знанням того, що таке StringUtilsякі в ньому є корисні методи. Ну і з усвідомленням, що є такі корисні речі і не потрібно городити щоразу мабоці в місцях, де можна було б закрити питання за допомогою готового рішення. Загалом ми розібрали лише частину методів. Якщо буде бажання, я можу продовжити: там їх ще багато, і вони реально заслуговують на увагу. Якщо є ідеї, як це ще можна подати, будь ласка, пишіть, я завжди відкритий до нових ідей. Документацію до методів написано дуже якісно, додано тестові приклади з результатами, що допомагає краще зрозуміти роботу методу. Тому не цураємось читання документації: вона розвіє ваші сумніви щодо функціоналу утиліти. Щоб отримати новий досвід кодингу, раджу подивитись, як роблять та пишуть утильні класи. Це буде корисно в майбутньому, тому що зазвичай на кожному проекті є свої практичні класи, і досвід їх написання стане в нагоді. Традиційно пропоную підписатися на гітхабі намій аккаунт ) Тих, хто не знає про мій проект з телеграм-ботом - ось посилання на першу статтю . Дякую всім за читання. Внизу додав кілька корисних посилань.Корисні посилання |
---|
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ