Java 자바 String 문자열 비교 String.equals() vs Objects.equals() / Objects 클래스의 equals() 메소드를 활용 이점(feat. validation)

기존의 문자열 비교

기존에 문자열 비교를 할 때는 당연히 String 클래스의 equals() 메소드를 사용하라고 암기적으로 외워왔을 것입니다. 

예를 들어,

"String 문자열의 경우 ==(참조값 비교) 가 아닌 equals() 메소드로 값 비교를 한다."

라는 식으로 말이죠.

 

이 문장은 자바 1.7까지는 유효했을 것입니다. String의 문자열이 일치하는지는 당연히 equals() 메소드로 확인이 가능했으니까요.

 

String 클래스의 equals() 메소드를 보겠습니다.

 

String equals() 메소드

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

뭐 간단히 살펴보면

1. 참조값 비교.

2. String으로 타입변환 가능 비교

3. 문자열 길이비교

4. charArray를 활용하여 직접 값비교

 

이렇게 4단계의 단계를 밟아 나가야 두 String 객체가 동일한 값을 갖는다는 것을 확인할 수 있죠.

근데 한가지 문제점이 있습니다.

 

String은 참조타입입니다. 

 

즉, 인스턴스가 존재해야합니다. 하지만 대부분의 자바개발자들은 이를 알고는 있지만 소스를 살펴보면 과연 참조타입처럼 사용하는지 의심스러울 때가 많습니다.

 

예를 살펴보겠습니다.

 

@RestController
public class BoardController{
	
    @PostMapping("/api/test/")
    public Object getApi(@RequestBody User user){
		if(user.name.equals("blackdog")){
        	//do something like business logic
        }
        return null;
    }

}

class User{
    public String name;
    public String age;
}

 

Controller의 파라미터로 User를 받았습니다. User 클래스에는 nameage라는 String 값을 저장할 수 있네요. "/api/test"의 파라미터로 name 과 age를 넘기면 User 객체에 매핑되어 user 변수에 참조값이 저장될 것입니다. 내부로직으로는 name 값을 이용해서 값비교를 하고 있습니다. 

 

잘못된 부분이죠. name으로 아무 값도 넘기지 않아 null값으로 매핑이 되었다면? -> NullPointerException이 발생하여 API가 동작하지 않을 것입니다.

 

누구나 예측할 수 있죠. 하지만 정신없이 코딩을 하다보면 이렇게 코딩하게 됩니다.

 

이 대안으로 나온 것이 모던자바 8부터 등장한 Objects 클래스입니다.

Objects 클래스에 관해 이전에 작성한 글이 있으니 참고해보시기 바랍니다. 이번 포스팅에서는 equals() 메소드 기준으로만 설명해보고자 합니다.

 

2019/12/17 - [소스까봤습니다] - Java Objects 클래스의 메소드들 까봤습니다!

 

안전한 문자열 비교 Objects 클래스의 equals() 메소드

Objects equals() 메소드

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

- 간단합니다. 결국에는 a와 b의 equals 비교를 하게 됩니다. 근데 한가지 중요한 부분은 두번째 괄호입니다.

( a != null && a.equals(b) )

 먼저 a의 null 값 체크를 한다는 점에서 NullPointerException의 예외 상황을 바로 처리해버리게 됩니다.

이부분에서 우선 큰 도움을 받았다고 생각했습니다. String 클래스의 인스턴스가 null 이아니어야 메소드 호출이 가능할 테니까요.

 

또 하나의 효율성은 Validation 체크입니다.

@Getter
@Setter
@ToString
@Builder
public class BoardParam {
    @Min(0)
    Long seq;
    @NotEmpty
    String content;
    @NotEmpty
    String username;
    @NotEmpty
    String title;
}

Spring 프레임워크를 사용하신다면 @NotEmpty나 @NotNull과 같은 @Valid 어노테이션으로 유효성을 체크하는 어노테이션을 많이들 접해보셨을겁니다.

 

이는 null이나 빈문자열("") 등을 걸러내어 비즈니스 로직에서 좀더 집중적인 코딩을 위한 인터페이스들 인데요.

 

뭐 NotNull 체크나 빈문자열 체크가 된다면 더없이 좋을테지만. 항상 Null이 아닌 혹은 빈문자열이 아니어야 하는 경우는 없을 것입니다. 예외적인 허용상황이 존재할 테니까요. 뭐 예를들어, 게시판의 이미지업로드의 경우 Nullable 할 것입니다.

 

파일명을 저장한 String 변수에서 메소드를 호출하는 경우 예외상황이 발생할테죠.

 

저는 이러한 상황도 굳이 만들지 않도록 Objects 클래스의 equals() 메소드가 해결했다고 생각합니다.

 

String 클래스의 기존 알고리즘도 그대로 활용하구요! 물론 추가적인 로직이 조금더  추가되었지만 software의 퍼포먼스에 critical하다고 생각하진 않습니다. 

 

개인적으로 Java 8버전이 많이 대중화된 지금 아직도 모던자바 이전의 코딩방식을 고수하면서 자바 버전만 업그레이드한 곳이 많다고 생각됩니다. 버전을 업그레이드 한 만큼 성능좋은 제품을 써야지 기존 제품이 좋고 익숙하다고 멈춰 있어서는 안될 것 같다는게 저의 생각입니다.

댓글

Designed by JB FACTORY