JavaRush /Java Blog /Random-TL /Hatiin natin ang klase ng StringUtils

Hatiin natin ang klase ng StringUtils

Nai-publish sa grupo
Kumusta sa lahat, mahal kong mga mambabasa. Sinusubukan kong magsulat tungkol sa kung ano ang talagang interesado sa akin at kung ano ang nag-aalala sa akin sa sandaling ito. Samakatuwid, ngayon ay magkakaroon ng ilang magaan na pagbabasa na magiging kapaki-pakinabang sa iyo bilang isang sanggunian sa hinaharap: pag-usapan natin ang tungkol sa StringUtils . Разбираем по полочкам класс StringUtils - 1Nagkataon lang na minsan ay nalampasan ko ang library ng Apache Commons Lang 3 . Ito ay isang library na may mga auxiliary na klase para sa pagtatrabaho sa iba't ibang mga bagay. Ito ay isang koleksyon ng mga kapaki-pakinabang na pamamaraan para sa pagtatrabaho sa mga string, mga koleksyon, at iba pa. Sa isang kasalukuyang proyekto, kung saan kailangan kong magtrabaho nang mas detalyado gamit ang mga string sa pagsasalin ng 25 taong gulang na lohika ng negosyo (mula sa COBOL hanggang Java), lumabas na wala akong sapat na malalim na kaalaman sa klase ng StringUtils . Kaya kinailangan kong likhain ang lahat sa aking sarili. Ang ibig kong sabihin? Ang katotohanan na hindi mo kailangang magsulat ng ilang mga gawain na kinasasangkutan ng pagmamanipula ng string sa iyong sarili, ngunit gumamit ng isang handa na solusyon. Ano ang mali sa pagsulat nito sa iyong sarili? Hindi bababa sa na ito ay higit pang code na naisulat nang matagal na ang nakalipas. Ang hindi gaanong pagpindot ay ang isyu ng pagsubok sa code na isinulat din. Kapag gumamit kami ng library na napatunayang mabuti, inaasahan namin na nasubok na ito at hindi na namin kailangang magsulat ng isang grupo ng mga test case para subukan ito. Nagkataon lang na ang hanay ng mga pamamaraan para sa pagtatrabaho sa isang string sa Java ay hindi masyadong malaki. Talagang hindi maraming mga pamamaraan na magiging kapaki-pakinabang para sa trabaho. Ang klase na ito ay nilikha din upang magbigay ng mga tseke para sa NullPointerException. Ang balangkas ng aming artikulo ay ang mga sumusunod:
  1. Paano kumonekta?
  2. Mga halimbawa mula sa aking trabaho: kung paano, nang hindi nalalaman ang tungkol sa isang kapaki-pakinabang na klase, ginawa ko ang aking saklay ng bisikleta .
  3. Tingnan natin ang iba pang mga pamamaraan na nakita kong kawili-wili.
  4. I-summarize natin.
Ang lahat ng mga kaso ay idaragdag sa isang hiwalay na repositoryo sa organisasyon ng Javarush Community sa GitHub. Magkakaroon ng hiwalay na mga halimbawa at pagsubok para sa kanila.

0. Paano kumonekta

Ang mga naglalakad na magkahawak-kamay sa akin ay halos pamilyar na sa Git at Maven, kaya higit pa ay aasa ako sa kaalamang ito at hindi na uulitin ang aking sarili. Para sa mga nakaligtaan ang aking mga naunang artikulo o nagsimula pa lang magbasa, narito ang mga materyales tungkol sa Maven at Git . Siyempre, nang walang isang build system (Maven, Gredl), maaari mo ring ikonekta ang lahat nang manu-mano, ngunit ito ay baliw sa ngayon at tiyak na hindi mo kailangang gawin ito nang ganoon: mas mahusay na agad na matutunan kung paano gawin ang lahat ng tama. Samakatuwid, upang gumana sa Maven, idinagdag muna namin ang naaangkop na dependency:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Kung saan ang ${apache.common.version} ay ang bersyon ng library na ito. Susunod, upang mag-import sa ilang klase, magdagdag ng pag-import:
import org.apache.commons.lang3.StringUtils;
At ayun, nasa bag na lahat))

1. Mga halimbawa mula sa isang tunay na proyekto

  • paraan ng leftPad

Первый пример вообще сейчас кажется настолько глупым, что очень хорошо, что мои коллеги знали о StringUtils.leftPad и подсказали мне. Какая была задача: code был построен так, что нужно было сделать трансформацию данных, если они пришли не совсем корректно. Ожидалось, что строковое поле должно состоять только из цифр, т.е. если его длина его 3, а meaning — 1, то запись должно быть “001”. То есть, сперва нужно удалить все пробелы, а потом замостить уже это нулями. Еще примеров, чтобы была понятна суть задачи: из “12 “ -> “012” из “1 “ -> “001” И так далее. What сделал я? Описал это в классе 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();
}
За основу взял идею, что мы можем просто получить разницу между оригинальным и обрезанным meaningм и заполнить впереди нулями. Для этого я использовал IntStream, чтобы n раз сделать одну и ту же операцию. И это точно нужно тестировать. А вот что можно было сделать, если бы я знал заранее о методе StringUtils.leftPad:
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Как видите, codeа намного меньше, при этом еще и используется всеми подтвержденная библиотека. Для этого дела я создал два теста в классе LeftPadExampleTest (обычно когда планируют тестировать Howой-то класс, создают в таком же пакете, только в src/test/java, класс с таким же именем+Test). Эти тесты проверяют то один метод, чтобы он правильно трансформировал meaning, то другой. Конечно, тестов нужно бы написать намного больше, но тема тестирования в нашем случае не главная:
package com.github.javarushcommunity.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:
  1. Тест будет восприниматься How тест, если у него есть соответствующая annotation — @Test.
  2. Если в имени сложно описать работу теста or же описание длинное и его неудобно читать, можно добавить аннотацию @DisplayName и сделать в ней нормальное описание, которое будет видно при запуске тестов.
  3. При написании тестов я использую BDD подход, в котором разделяю тесты на логические части:
    1. //given — блок настройки данных перед тестом;
    2. //when — блок, где запускается та часть codeа, которую мы тестируем;
    3. //then — блок, в котором проходят проверки результатов работы блока when.
Если их запустить, они подтвердят, что все работает How ожидается.

  • stripStart метод

Здесь мне нужно было решить вопрос со строкой, в начале которой могли быть пробелы и запятые. После трансформации их не должно было быть в новом значении. Постановка задачи ясна How никогда. Несколько примеров закрепит наше понимание: “, , 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);
}
Здесь мы передаем первым аргументом информацию о том, с Howой строкой работаем, а во втором передаем строку, состоящую из символов, которые нужно пропустить. Точно так же создаем StripStartExampleTest класс:
package com.github.javarushcommunity.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. Зачем? Whatбы не было NullPointerException, то есть, во избежание вызова методов переменной, которая является null. Поэтому чтобы не писать:
if(value != null && value.isEmpty()) {
   //doing something
}
Можно просто сделать так:
if(StringUtils.isEmpty(value)) {
   //doing something
}
Плюс этого метода в том, что сразу видно где Howой метод используются.

2. Разбор других методов класса StringUtils

Теперь поговорим о тех методах, которые на мой взгляд тоже заслуживают внимания. Говоря в общем о StringUtils, стоит сказать, что он предоставляет null безопасные методы-аналоги тех, что есть в классе String (How в случае с методом isEmpty). Пройдемся по ним:

  • compare метод

Такой метод есть в String и будет NullPointerException, если в сравнении двух строк одна из них будет null. Whatбы избежать уродских проверок в нашем codeе, можем использовать метод StringUtils.compare(String str1, String str2): он возвращает int How результат сравнения. What обозначают эти значения? int = 0, если они одинаковы (or оба null). int < 0, if str1 меньше, чем str2. int > 0, if str1 больше, чем str2. Также если посмотреть на их documentацию, то в 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... методы

Здесь разработчики утorты разгулялись на славу. Какой хочешь метод есть. Я решил их собрать воедино:
  1. contains — метод, проверяющий, есть ли предполагаемая строка внутри другой строки. Чем это полезно? Можно использовать этот метод, если нужно удостовериться, что есть Howое-то слово в тексте.

    Примеры:

    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) безопасность присутствует.

  2. containsAny — метод, проверяющий, есть ли хоть Howой-то символ из представленных в строке. Также полезная вещь: часто приходится такое выполнять.

    Примеры из documentации:

    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

  3. containsIgnoreCase — полезное расширение для метода contains. Действительно, чтобы проверить такой случай без этого метода, придется перебрать несколько вариантов. А так гармонично будет использован только один метод.

  4. Несколько примеров из доки:

    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

  5. containsNone — уже судя по названию можно понять, что проверяется. Строк внутри не должно быть. Полезная вещь, определенно. Быстрый поиск Howих-то неугодных символов ;). В нашем телеграм-боте будем фильтровать маты, не пройдем мимо этих забавных методов.

    И примеры, куда ж без них:

    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

  • defaultString метод

Серия методов, которые помогают избежать добавления лишнего ифчика в случае, если строка null и нужно поставить Howое-то meaning по умолчанию. Вариантов есть много, на любой вкус. Главный среди них — StringUtils.defaultString(final String str, final String defaultStr) — в случае, если str равно null, мы просто передадим defaultStr meaning. Примеры из documentации:
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 метод

Говорит сам за себя. Это очень полезный метод: он проверяет, заканчивается ли строка предлагаемой строкой or нет. Часто такое такое нужно. Конечно можно написать проверку и самому, но использовать уже готовый метод явно удобнее и лучше. Примеры:
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) просто идет How бонус, ведь это ж абсурд) Также есть там и метод, который игнорирует регистр.

  • equals метод

Отличный пример null безопасного метода, который сравнивает две строки. What бы мы туда ни положor, ответ будет, и будет без ошибок. Примеры:
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. Допустим, instead of нескольких проверок на equalsство, мы хотим выполнить одну. Вот для этого мы можем передать строку, с которой будут сравнивать и набор строк, если Howая-то из них будет равна предлагаемой — то будет 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, Howие в нем есть полезные методы. Ну и с осознанием, что есть вот такие полезные вещи и не нужно городить каждый раз костыли в местах, где можно было бы закрыть вопрос при помощи готового решения. В целом мы разобрали только часть методов. Если будет желание, я могу продолжить: там их еще много, и они реально заслуживают внимания. Если есть идеи, How это еще можно подать, пожалуйста, пишите — я всегда открыт к новым идеям. Документация к методам написана очень качественно, добавлены тестовые примеры с результатами, что помогает лучше понять работу метода. Поэтому не чураемся чтения documentации: она развеет ваши сомнения по поводу функционала утorты. Whatбы получить новый опыт codeинга, советую посмотреть, How делают и пишут утильные классы. Это будет полезно в будущем, так How обычно на каждом проекте есть свои утильные классы, и опыт их написания пригодится. Традиционно предлагаю подписаться на гитхабе на мой аккаунт) Тех, кто не знает о моем проекте с телеграмм-ботом — вот link на первую статью. Всем спасибо за чтение. Внизу добавил несколько полезных ссылок.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION