equals 를 사용할 때 유의점

joona95·2025년 1월 5일
0

최근에 실수로 인해 의도치 않게 장애를 냈는데 다른 부분까지 영향을 줘서 수습한 경험을 했다.

내부적으로 equals 가 다르게 작동하면서 발생한 문제였어서 이에 대해 좀 정리를 해서 기록해보고자 한다.


ArrayList.contains(Object o) 내부에서는 Object.equals(Object o) 비교를 통해 리스트 내에 해당 값이 존재하고 있는지 확인하고 있다.


List<ABC> a = new ArrayList([new ABC("1"), new ABC("2")]);
ABC b = new ABC("1");

a.contains(b);
ABC a = new ABC("1");
ABC b = new ABC("1");

a.equals(b);

그래서 위와 같은 코드에서 contains 내부에서 작동하는 equals 가 아래 코드의 equals 와 동일한 동작을 원래는 해야했다.

그러나 객체 내부에 equals 재정의가 되어 있어서 문제가 발생하게 된 것이다.

equals를 재정의가 되어 있다고 하더라도 equals(Object o) 로 하면 동일하게 작동을 했을 것이나 내부에 재정의된 메서드는 equals(ABC abc) 였던 것이었다.

그런데 또 생각해보니 사실상 재정의라기보다는 오버로딩인가 싶기도 하다.


어쨌든 이 상황을 해결하기 위해서는 아예 equals(Object o) 로 재정의하거나 equals(ABC abc) 내부에 같은 방식으로 동작하기 위한 로직을 추가해줘야 했다.

기존에 돌아가고 있는 로직이 이미 있고 다른 곳에 영향도가 얼마나 미칠지가 명확하지 않은 상황에서 고민하다가 후자를 선택하기로 했다.


@EqualsAndHashCode(of = {"def"})
class ABC {
	DEF def;
    
    public boolean equals(ABC abc) {
    	...
	}
}
@EqualsAndHashCode(of = {"str"})
class DEF {
	@NotNull
	String str;
}

클래스 내부에서는 Object.equals 의 경우 @EqualsAndHashCode 로 또 재정의되어 있는 상태였다.

재정의된 equals(ABC abc) 내부에 같은 방식으로 동작하기 위한 로직을 추가한다고 했을 때 어떤 값을 비교하도록 해야할지도 고민이 있었다.

DTO 자체를 비교하는 def.equals(abc.getDef()) 를 추가해야 할지, 아니면 DTO 내부 값을 비교하는 def.getStr().equals(abc.getDef().getStr()) 를 추가해야 할지가 고민이었다.

처음에는 내부에 어차피 @EqualsAndHashCode 에서 Object.equals() 를 재정의하고 있기도 하고 동일한 equals 로직을 타는 게 좋을 것 같으니 object 비교를 해야겠다고 생각을 했다.

그러나 Object 간의 비교로 처리하는 경우 무언가 내부적으로 equals 정의가 변경되면 의도했던 바와 다르게 작동할 수 있다는 문제가 있었다.

그래서 웬만하면 Object 간의 비교는 하지 않는 것이 좋다고 하여 내부 값 자체를 비교하는 방식으로 변경하였다.


@EqualsAndHashCode(of = {"def"})
class ABC {
	DEF def;
    
    public boolean equals(ABC abc) {
    	...
        
        if (def != null && abc.getDef() != null && !StringUtils.equals(def.getStr(), abc.getDef().getStr())) {
        	return false;
        }
        return true;
	}
}
@EqualsAndHashCode(of = {"str"})
class DEF {
	@NotNull
	String str;
}

결론

  • 정말 필요한 경우가 아니라면 equals 재정의하는 건 피하도록 하자.
  • equals 를 재정의하는 게 필요할 때는 일반 규약에 맞춰서 재정의하고 equals 와 hashcode 를 동시에 정의해야 한다.
  • Object 간 비교보다는 내부 값이 같은지 비교하여 클래스 정보 변경으로 인한 의도하지 않은 동작이 생기는 걸 방지하도록 하자.

0개의 댓글