JavaRush /Blogue Java /Random-PT /Vamos analisar a classe StringUtils
Roman Beekeeper
Nível 35

Vamos analisar a classe StringUtils

Publicado no grupo Random-PT
Olá a todos, meus queridos leitores. Procuro escrever sobre o que realmente me interessa e o que me preocupa no momento. Portanto, hoje haverá uma leitura leve que será útil para você como referência no futuro: vamos falar sobre StringUtils . Vamos analisar a classe StringUtils - 1Acontece que certa vez eu ignorei a biblioteca Apache Commons Lang 3 . Esta é uma biblioteca com classes auxiliares para trabalhar com diversos objetos. Esta é uma coleção de métodos úteis para trabalhar com strings, coleções e assim por diante. Em um projeto atual, onde tive que trabalhar mais detalhadamente com strings na tradução de lógica de negócios de 25 anos (de COBOL para Java), descobri que eu não tinha um conhecimento profundo o suficiente da classe StringUtils . Então eu tive que criar tudo sozinho. O que eu quero dizer? O fato de que você não precisa escrever certas tarefas que envolvem manipulação de strings, mas usar uma solução pronta. O que há de errado em escrever você mesmo? Pelo menos no sentido de que se trata de mais código que já foi escrito há muito tempo. Não menos urgente é a questão de testar o código escrito adicionalmente. Quando usamos uma biblioteca que provou ser boa, esperamos que ela já tenha sido testada e que não precisemos escrever um monte de casos de teste para testá-la. Acontece que o conjunto de métodos para trabalhar com strings em Java não é tão grande. Na verdade, não existem muitos métodos que seriam úteis para o trabalho. Esta classe também foi criada para fornecer verificações para NullPointerException. O esboço do nosso artigo será o seguinte:
  1. Como conectar?
  2. Exemplos do meu trabalho: como, sem conhecer uma aula tão útil, criei minha muleta para bicicleta .
  3. Vejamos outros métodos que achei interessantes.
  4. Vamos resumir.
Todos os casos serão adicionados a um repositório separado na organização da comunidade Javarush no GitHub. Haverá exemplos e testes separados para eles.

0. Como se conectar

Aqueles que andam de mãos dadas comigo já estão mais ou menos familiarizados com Git e Maven, então continuarei confiando nesse conhecimento e não me repetindo. Para quem perdeu meus artigos anteriores ou apenas começou a ler, aqui estão materiais sobre Maven e Git . Claro, sem um sistema de compilação (Maven, Gredl), você também pode conectar tudo manualmente, mas isso é uma loucura hoje em dia e você definitivamente não precisa fazer assim: é melhor aprender imediatamente como fazer tudo corretamente. Portanto, para trabalhar com o Maven, primeiro adicionamos a dependência apropriada:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Onde ${apache.common.version} é a versão desta biblioteca. A seguir, para importar em alguma classe, adicione import:
import org.apache.commons.lang3.StringUtils;
E é isso, está tudo na bolsa))

1. Exemplos de um projeto real

  • método leftPad

O primeiro exemplo geralmente parece tão estúpido agora que é muito bom que meus colegas conhecessem StringUtils.leftPad e me contassem. Qual era a tarefa: o código foi construído de tal forma que era necessário transformar os dados caso eles não chegassem corretamente. Esperava-se que o campo string consistisse apenas em números, ou seja, se seu comprimento for 3 e seu valor for 1, a entrada deverá ser “001”. Ou seja, primeiro você precisa remover todos os espaços e depois cobri-los com zeros. Mais exemplos para deixar clara a essência da tarefa: de “12“ -> “012” de “1“ -> “001” E assim por diante. O que eu fiz? Descrito isso na classe LeftPadExample . Eu escrevi um método que fará tudo isso:
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();
}
Como base, tomei a ideia de que podemos simplesmente obter a diferença entre o valor original e o valor aparado e preenchê-la com zeros na frente. Para fazer isso usei o IntStream para fazer a mesma operação n vezes. E isso definitivamente precisa ser testado. Aqui está o que eu poderia ter feito se soubesse antecipadamente sobre o método StringUtils.leftPad :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Como você pode ver, há muito menos código e também é usada uma biblioteca confirmada por todos. Para isso criei dois testes na classe LeftPadExampleTest (geralmente quando planejam testar uma classe, eles criam uma classe com o mesmo nome + Teste no mesmo pacote, apenas em src/test/java). Esses testes verificam um método para garantir que ele transforma corretamente o valor e depois outro. É claro que muito mais testes precisariam ser escritos, mas testes não é o tópico principal no nosso caso:
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);
   }

}
Posso fazer alguns comentários sobre os testes por enquanto. Eles são escritos usando JUnit 5:
  1. Um teste será tratado como teste se tiver a anotação apropriada - @Test.
  2. Se for difícil descrever o funcionamento do teste no nome ou se a descrição for longa e difícil de ler, você pode adicionar a anotação @DisplayName e torná-la uma descrição normal que ficará visível durante a execução dos testes.
  3. Ao escrever testes, utilizo a abordagem BDD, na qual divido os testes em partes lógicas:
    1. //dado - bloco de configuração de dados antes do teste;
    2. //quando é o bloco onde é lançada a parte do código que estamos testando;
    3. //then é um bloco no qual os resultados do bloco when são verificados.
Se você executá-los, eles confirmarão que tudo está funcionando conforme o esperado.

  • método stripStart

Aqui eu precisava resolver um problema com uma linha que poderia ter espaços e vírgulas no início. Após a transformação, eles não deveriam ter um novo significado. A declaração do problema está mais clara do que nunca. Alguns exemplos reforçarão nosso entendimento: “, , books” -> “books” “,,, books” -> “books” b , books” -> “b , books” Como no caso do leftPad, adicionei o Classe StrimStartExample , na qual possui dois métodos. Um - com solução própria:
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);
}
Aqui a ideia era encontrar o índice a partir do qual não existem mais espaços ou vírgulas. Se eles não estivessem lá no início, o índice será zero. E o segundo - com solução via StringUtils :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Aqui passamos no primeiro argumento informações sobre com qual string estamos trabalhando, e no segundo passamos uma string composta por caracteres que precisam ser ignorados. Criamos a classe StripStartExampleTest da mesma maneira :
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);
   }
}

  • Método isEmpty

Este método, claro, é muito mais simples, mas isso não o torna menos útil. Ele estende os recursos do método String.isEmpty() , que também adiciona uma verificação de nulo. Para que? Para evitar NullPointerException, ou seja, para evitar chamar métodos em uma variável que seja null . Portanto, para não escrever:
if(value != null && value.isEmpty()) {
   //doing something
}
Você pode simplesmente fazer isso:
if(StringUtils.isEmpty(value)) {
   //doing something
}
A vantagem deste método é que fica imediatamente claro onde qual método é usado.

2. Análise de outros métodos da classe StringUtils

Agora vamos falar daqueles métodos que, na minha opinião, também merecem atenção. Falando de maneira geral sobre StringUtils , vale dizer que ele fornece métodos nulos seguros análogos aos encontrados na classe String (como é o caso do método isEmpty ). Vamos examiná-los:

  • método de comparação

Tal método existe em String e lançará uma NullPointerException se, ao comparar duas strings, uma delas for nula. Para evitar verificações feias em nosso código, podemos usar o método StringUtils.compare(String str1, String str2) : ele retorna um int como resultado da comparação. O que esses valores significam? int = 0 se forem iguais (ou ambos forem nulos). int < 0, se str1 for menor que str2. int > 0, se str1 for maior que str2. Além disso, se você consultar a documentação, o Javadoc deste método apresenta os seguintes cenários:
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

  • contém... métodos

Aqui, os desenvolvedores de utilitários se divertiram muito. Qualquer método que você quiser está lá. Resolvi juntá-los:
  1. contém é um método que verifica se a string esperada está dentro de outra string. Como isso é útil? Você pode usar este método se precisar ter certeza de que há uma determinada palavra no texto.

    Exemplos:

    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

    Novamente, a segurança NPE (Null Pointer Exception) está presente.

  2. containsAny é um método que verifica se algum dos caracteres presentes na string está presente. Também é uma coisa útil: muitas vezes você precisa fazer isso.

    Exemplos da documentação:

    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 é uma extensão útil para o método contains . Na verdade, para verificar tal caso sem este método, você terá que passar por várias opções. E assim apenas um método será utilizado harmoniosamente.

  4. Alguns exemplos dos documentos:

    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 - a julgar pelo nome, você já pode entender o que está sendo verificado. Não deve haver linhas dentro. Uma coisa útil, definitivamente. Pesquisa rápida por alguns caracteres indesejados;). Em nosso bot de telegramas filtraremos obscenidades e não ignoraremos esses métodos engraçados.

    E exemplos, onde estaríamos sem eles:

    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

  • método defaultString

Uma série de métodos que ajudam a evitar a adição de informações extras se a string for nula e você precisar definir algum valor padrão. São muitas opções para todos os gostos. O principal deles é StringUtils.defaultString(final String str, final String defaultStr) - caso str seja nulo, simplesmente passaremos o valor para defaultStr . Exemplos da documentação:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
É muito conveniente usar ao criar uma classe POJO com dados.

  • método deleteWhitespace

Este é um método interessante, embora não existam muitas opções para sua aplicação. Ao mesmo tempo, se tal caso surgir, o método será definitivamente muito útil. Remove todos os espaços da string. Onde quer que esteja essa lacuna, não haverá nenhum vestígio dela))) Exemplos dos documentos:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • termina com método

Fala por si. Este é um método muito útil: verifica se a string termina com a string sugerida ou não. Muitas vezes isso é necessário. Claro, você mesmo pode preencher o cheque, mas usar um método pronto é claramente mais conveniente e melhor. Exemplos:
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
Como você pode ver, tudo termina com uma linha vazia))) Acho que esse exemplo (StringUtils.endsWith("ABCDEF", "") = true) é só um bônus, porque isso é um absurdo) Também existe um método que ignora caso.

  • método igual

Um ótimo exemplo de método nulo seguro que compara duas strings. O que quer que coloquemos lá, a resposta estará lá e sem erros. Exemplos:
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
Claro, também existe equalsIgnoreCase - tudo é feito exatamente da mesma maneira, só que ignoramos o caso. Vamos ver?
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

  • método equalsAny

Vamos prosseguir e estender o método equals . Digamos que em vez de várias verificações de igualdade, queremos realizar uma. Para isso, podemos passar uma string com a qual será comparado um conjunto de strings; se alguma delas for igual à proposta, será VERDADEIRO. Passamos uma string e uma coleção de strings para compará-las (a primeira string com as strings da coleção). Difícil? Aqui estão exemplos dos documentos para ajudá-lo a entender o que isso significa:
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
Também existe equalsAnyIgnoreCase . E exemplos para isso:
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

Resultado final

Como resultado, saímos com o conhecimento do que é StringUtils e quais métodos úteis ele possui. Pois bem, com a constatação de que existem coisas tão úteis e não há necessidade de cercar cada vez com muletas os locais onde seria possível encerrar o problema com a ajuda de uma solução pronta. Em geral, analisamos apenas parte dos métodos. Se quiser, posso continuar: há muitos mais deles e realmente merecem atenção. Se você tiver alguma ideia sobre como isso poderia ser apresentado, por favor escreva - estou sempre aberto a novas ideias. A documentação dos métodos está muito bem escrita, são adicionados exemplos de testes com resultados, o que ajuda a entender melhor o funcionamento do método. Portanto, não hesitamos em ler a documentação: ela tirará suas dúvidas sobre a funcionalidade do utilitário. Para obter uma nova experiência de codificação, aconselho você a observar como as classes utilitárias são criadas e escritas. Isso será útil no futuro, já que normalmente cada projeto tem suas próprias classes de sucata, e a experiência em escrevê-las será útil. Tradicionalmente, sugiro que você assine minha conta no Github ) Para quem não conhece meu projeto com bot de telegrama, aqui está o link para o primeiro artigo . Obrigado a todos pela leitura. Adicionei alguns links úteis abaixo.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION