Object 클래스는 모든 클래스의 조상이다.
특징으로는 java 에 존재하는 모든 클래스는 Object 클래스를 상속하고있다.
오늘은 Object의 메소드 중에 equals() 와 hashCode() 를 다루겠다 🫡
동일성 vs 동등성 🤔
이 둘의 차이를 간단히 말하자면 동등성은 == 연산을 했을때 같은 것이고, 동일성은 우리가 같은 값이라고 정의한 것, 즉 equals() 메소드를 오버라이딩해서 비교하고 같은 값을 말한다
hashCode란 해싱 알고리즘에 의해 반환된 정수 값,HashSet과 같은 자료 구조에서 사용되는 Hash Table에서의 hashing을 가능하게 하는 것을 목적
HashSet같은 자료구조에서 같은 값인지 판별할 때는 hashCode() 의 결과값을 capacity(용량) 으로 % 모듈러 연산한 값을 비교한 후 equals() 를 통해서 비교한다.hashCode()로 먼저 비교하는 이유는 hashCode()가 equals()보다 가벼우며 이를 통해서 비교할 값의 후보군을 확 줄일 수 있기 때문이다 👍아래 코드로 HashSet의 예를 보겠다 👀
@AllArgsConstructor
public class Good {
int a;
String b;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Good good = (Good)obj;
return a == good.a && Objects.equals(b, good.b);
}
@Override
public int hashCode() {
return Objects.hash(a, b);
}
}
-> 여기서 오버라이딩한 equals()는 a, b 값이 같다면 동등함을 나타내고 hashCode() 는 같은 a, b 가 같다면 같은 해시코드를 리턴한다.
위 코드를 아래 코드로 적용해보자 👊
public class Hello {
public static void main(String[] args) {
Good good1 = new Good(1, "a");
Good good2 = new Good(1, "a");
Set<Good> set = new HashSet<>();
set.add(good1);
set.add(good2);
System.out.println("set size : " + set.size());
}
}
결과는 아래와 같다

equals(), hashCode()에 의해 두 객체가 같다고 판별되어 사이즈가 1인걸 볼 수 있다.
하지만 여기서 hashCode 메소드를 주석처리하면!

equals() 가 동작하더라도 hashCode()에서 다른 값이라고 판별되기 때문에 1의 사이즈가 나오는 것을 볼 수 있다.
이번에는 객체지향적인 설계에 있어서 강한 응집력, 약한 결합력 그리고 객체의 확실한 역할에 대해서 예시를 통해 알아보겠다.
지난 #1 포스팅의 Enum 활용과 이어지는 예다.
//Command Class
@Getter
@AllArgsConstructor
public class AppendCommand {
private AppendType appendType;
private String target;
private String source;
@RequiredArgsConstructor
public enum AppendType {
FRONT((a, b) -> b.concat(a)),
END(String::concat),
;
private final BiFunction<String, String, String> appender;
public String append(String str1, String str2) {
return this.appender.apply(str1, str2);
}
}
}
위 코드는 두 target, source 을 통해서 apendType이 FRONT 면 source 앞에 target 을 붙이고 END 면 source 뒤에 target 을 붙이는 간단한 예시다.
해당 클래스를 사용하려면 클래스를 사용하는 Client는 아래와 같은 코드를 가져야한다.
public class Client {
public String executeCommand(AppendCommand command) {
AppendType appendType = command.getAppendType();
String source = command.getSource();
String target = command.getTarget();
return appendType.append(source, target);
}
}
위 코드의 문제점은 무엇일까? 🤔
나는 아래 두 가지 문제점을 대표적으로 생각했다.
AppendCommand의 역할이 명확하지 않다Client가AppendCommand에 강하게 결합되어있다.
1번 문제에 대해 설명하자면 AppendCommand 라는 클래스를 설계 할 때는 분명 문자열을 append 해주는 역할을 수행하는 클래스로 설계했을 것이다.
하지만 실제로 AmmendCommand 는 append 를 해주는 역할을 명확하게 수행하지 않는다. 직접적으로 해당 역할을 수행하지 않고 append 하는데 필요한 재료들만 들고있는 셈이다.
다음으로 2번 문제에 대해 설명하자면 Client 는 AppendCommand 라는 객체만 들고있을 뿐이며, 그저 들고있는 AppendCommand 를 통해서 append 를 하고싶을 뿐이지 AppendCommand 가 들고있는 속성들에 대해서는 궁금하지 않다.
하지만 위 코드는
AppendType appendType = command.getAppendType();
String source = command.getSource();
String target = command.getTarget();
이런식으로 AppendCommand 의 속성을 가져오며 부수적인 속성들과 결합이 추가된다.
이렇게되면 AppendCommand 의 속성 혹은 호출 방법, 기타 등등 변화가 생긴다면 Client 도 코드를 수정해야하는 문제가 생긴다.
해결방법 💡
해결방법은 간단하다! 😀
불필요한 Getter 를 제거하고 AppendCommand 의 역할을 확실히 수행할 메소드를 만들어주면 된다.
해결방법을 적용하면
//Before
@Getter
@AllArgsConstructor
public class AppendCommand {
private AppendType appendType;
private String target;
private String source;
}
//After
@AllArgsConstructor
public class AppendCommand {
private AppendType appendType;
private String target;
private String source;
public String execute() {
return appendType.append(target, source);
}
}
→ Getter 를 제거하고 execute 라는 append 를 수행하는 메소드를 정의했다.
이로 인해 Client 에서는
//Before
public class Client {
public String executeCommand(AppendCommand command) {
AppendType appendType = command.getAppendType();
String source = command.getSource();
String target = command.getTarget();
return appendType.append(source, target);
}
}
//After
public class Client {
public String executeCommand(AppendCommand command) {
return command.execute();
}
}
→ AppendCommand 의 속성을 사용해 append 를 호출하는 코드 제거
이렇게 변경되면 Client 는 AppendCommand 와의 결합력이 약해지며 AppendCommand 의 응집도는 올라가고 역할 또한 명확해진다!! 😄
결과적으로 코드 가독성 또한 좋아지고 유지보수 또한 유리해지며 객체지향적 코드에 가까워질 수 있다 👍