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.11</version>
</dependency>

3. StringUtils 클래스

이 입문 요약에서 다룰 첫 번째 유틸리티 클래스는 StringUtils 입니다.

이름에서 알 있듯이  StringUtils를 사용하면 java.lang.String 이 즉시 제공하는 작업을 보완 / 확장하는 일련 의 null-safe s trings 작업 을 수행 할 수 있습니다 .

문자열 이 공백인지, 비어 있는지 , 소문자인지, 대문자인지, 영숫자 인지 확인하는 것과 같이 주어진 문자열 에 대해 여러 가지 검사를 수행하는 유틸리티 메소드 세트를 보여 드리겠습니다 .

@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 클래스입니다.

예상대로이 클래스는 숫자 유형을 처리하고 조작하는 것을 목표로하는 광범위한 유틸리티 메서드를 제공합니다 .

intlong같은 다른 프리미티브의 동등성을 비교하는 compare () 메서드 의 오버로드 된 구현을 살펴 보겠습니다 .

@Test
public void whenCalledcompareWithIntegers_thenCorrect() {
    assertThat(NumberUtils.compare(1, 1))
      .isEqualTo(0);
}
    
@Test
public void whenCalledcompareWithLongs_thenCorrect() {
    assertThat(NumberUtils.compare(1L, 1L))
      .isEqualTo(0);
}

또한 위의 예제와 매우 유사하게 작동하는 byteshort 에서 작동하는 compare () 구현이 있습니다 .

이 리뷰의 다음은 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. Lazy 초기화와 빌더 클래스

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 객체 를 얻으려면 UserInitializer의 get () 메서드를 호출하면됩니다 .

@Test 
public void whenCalledget_thenCorrect() 
  throws ConcurrentException { 
    UserInitializer userInitializer = new UserInitializer(); 
    assertThat(userInitializer.get()).isInstanceOf(User.class); 
}

GET () 에 규정 된 방법은 인스턴스 필드의 이중 체크 관용구 (스레드 안전)의 구현입니다 조슈아 블로흐의 "효과적인 자바", 항목 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 클래스를 구현합니다 .이를 통해 일반적인 유창한 API를 기반으로 빌더에 다른 매개 변수를 제공하여 hashCode () 구현 을 생성 할 수 있습니다 .

@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에서 일류 시민입니다.

라이브러리에는 여러 리플렉션 클래스가 포함되어있어 클래스 필드 및 메서드에 반사적으로 액세스하고 조작 할 수 있습니다.

예를 들어, 순진한 User 도메인 클래스를 구현했다고 가정 해 보겠습니다 .

public class User {

    private String name;
    private String email;
    
    // standard constructors / getters / setters / toString
}

매개 변수화 된 생성자가 public 이라고 가정하면 ConstructorUtils 클래스를 사용하여 쉽게 액세스 할 수 있습니다 .

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

생성자를 통한 표준 클래스 인스턴스화 대신 invokeConstructor ()invokeExactConstructor () 메서드를 호출하여 반사적으로 User 인스턴스를 만들 수 있습니다 .

@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 클래스를 사용하여 클래스 메서드에 대한 리플렉션을 사용할 수 있습니다 .

이 경우 User 클래스의 getName () 메서드 의 가시성 public 입니다. 따라서 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. MutableObject 클래스

불변성 은 가능한 모든 경우에 기본값으로 설정해야하는 좋은 객체 지향 소프트웨어의 핵심 기능 이지만 , 안타깝게도 때때로 변경 가능한 객체를 처리해야합니다.

또한 변경 가능한 클래스를 만들려면 많은 상용구 코드가 필요하며 대부분의 IDE에서 자동 생성 된 setter를 통해 생성 할 수 있습니다.

이를 위해 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입니다.

표준 setter / getter를 통해 쌍으로 래핑 된 왼쪽 및 오른쪽 개체를 설정하고 액세스 할 수 있습니다.

14. ImmutablePair 클래스

당연히 ImmutablePair 라는 MutablePair 클래스 의 불변의 대응 구현도 있습니다 .

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 입니다.

클래스가 추상이므로 of () 정적 팩토리 메서드 를 사용하여 Triple 인스턴스를 만들 수 있습니다 .

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

MutableTripleImmutableTriple 클래스를 통해 변경 가능한 트리플과 변경 불가능한 트리플 모두에 대한 구체적인 구현도 있습니다.

정적 팩토리 메서드가 아닌 매개 변수화 된 생성자를 통해 인스턴스를 만들 수 있습니다.

이 경우 API가 MutablePairImmutablePair 클래스 의 API와 매우 유사 해 보이므로 건너 뛰겠습니다 .

16. 결론

이 예제에서, 우리는 아파치 코 몬즈 랭 3가 제공하는 가장 유용한 유틸리티 클래스의 일부에서 자세히 살펴했다 떨어져 선반 .

라이브러리는 살펴볼 가치가있는 다른 많은 유틸리티 클래스를 구현 합니다. 여기에서 우리는 매우 독단적 인 기준에 따라 가장 유용한 것을 보여주었습니다.

전체 라이브러리 API는 공식 Javadocs 를 확인하십시오 .

평소처럼이 사용방법(예제)에 표시된 모든 코드 샘플은 GitHub에서 사용할 수 있습니다 .