반응형
인텔리제이에서 equals()나 hashCode()를 생성할때 instanceof와 getClass 중에 어느걸 사용할꺼냐 선택하는 경우가 있다.
instanceof 방식
- 상속 관계를 허용.
- 부모 클래스와 자식 클래스 간의 비교가 가능.
- 더 유연하지만 대칭성(symmetry) 원칙을 위반할 수 있다.
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof MyClass)) return false;
MyClass myClass = (MyClass) obj;
// 필드 비교 로직
return Objects.equals(name, myClass.name);
}
getClass() 방식
- 정확히 같은 클래스인 경우에만 true를 반환.
- 상속 관계에서도 부모와 자식은 다른 것으로 취급.
- equals 계약의 대칭성을 보장.
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
MyClass myClass = (MyClass) obj;
// 필드 비교 로직
return Objects.equals(name, myClass.name);
}
언제 어떤 것을 선택해야 할까?
- instanceof
- 상속 구조에서 부모-자식 간 비교가 필요한 경우
- 인터페이스 기반 비교가 필요한 경우
- getClass()
- 엄격한 타입 체크가 필요한 경우
- equals 계약의 대칭성을 확실히 보장하고 싶은 경우
- 일반적으로 권장되는 방식
예시
DTO 같은 데이터 클래스에서는 getClass() 방식을 쓰는 것이 좋다.
DTO는 주로
- API 요청/응답 데이터
- 데이터베이스 매핑
- 계층 간 데이터 전송
이런 용도로 사용되는데 정확한 타입 매칭이 중요하기 때문에, 부모가 같다고 같은 인스턴스라고 쳐버리면 안됨.
DTO는 보통 정확히 같은 구조의 데이터를 나타내므로, 클래스가 다르면 다른 것으로 취급하는 것이 맞다.
public class UserDto {
private String name;
private int age;
// getClass() 방식 사용
}
public class AdminDto extends UserDto {
private String role;
// 상속받았지만 UserDto와는 다른 객체로 취급
}
UserDto user = new UserDto("김철수", 25);
AdminDto admin = new AdminDto("김철수", 25, "ADMIN");
// getClass() 방식: false (올바름)
// instanceof 방식: true (위험할 수 있음)
instanceof 방식은 상속 구조에서 다형성을 활용하고 싶을 때 사용한다.
1. 추상 클래스/인터페이스 기반 설계
// 도형 클래스 계층
public abstract class Shape {
protected String color;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Shape)) return false;
Shape shape = (Shape) obj;
return Objects.equals(color, shape.color);
}
}
public class Circle extends Shape {
private double radius;
// color가 같고 radius가 같으면 동일로 취급
}
public class Rectangle extends Shape {
private double width, height;
// color가 같고 width, height가 같으면 동일로 취급
}
위 처럼 같은 도형군(Shape)으로 취급하고 싶다면 instanceof를 사용한다.
2. 전략 패턴에서의 활용
public interface PaymentMethod {
void pay(int amount);
// 같은 결제 수단으로 취급
default boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof PaymentMethod)) return false;
// 구체적인 구현체가 달라도 같은 인터페이스면 비교 가능
return isSamePaymentType(obj);
}
}
public class CreditCard implements PaymentMethod { }
public class DebitCard implements PaymentMethod { }
3. 컬렉션/컨테이너 클래스
public abstract class Animal {
protected String name;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Animal)) return false;
Animal animal = (Animal) obj;
return Objects.equals(name, animal.name);
}
}
public class Dog extends Animal { }
public class Cat extends Animal { }
// 사용 예시
Dog dog = new Dog("멍멍이");
Cat cat = new Cat("멍멍이"); // 이름이 같은 고양이
// instanceof 방식: true (같은 동물로 취급)
// getClass() 방식: false (다른 종류의 동물)
instanceof 선택 기준
- 의미적으로 같은 범주로 취급하고 싶을 때
- 다형성을 활용한 설계일 때
- 인터페이스나 추상 클래스 기반 비교가 필요할 때
어떻게 보면 '비즈니스 로직상 어떻게 같은 것으로 취급할 것인가?' 를 생각해보면 좋을 것 같다.
// instanceof가 적합한 경우
public abstract class Vehicle {
// 모든 차량을 동일한 기준으로 비교하고 싶을 때
}
// getClass()가 적합한 경우
public class UserRequestDto {
// 정확히 같은 DTO 타입이어야 할 때
}
- DTO는 데이터 구조가 정확해야 하므로 getClass()
- 도메인 객체는 개념적 동일성이 중요하므로 instanceof
반응형
'개발 아카이브 > JAVA' 카테고리의 다른 글
Spring 기본 - 게시판 만들기 (0) | 2025.09.21 |
---|---|
Spring 기본 - DTO (Data Transfer Object) 패턴 (0) | 2025.09.20 |
Spring 기본 - Service 계층 (0) | 2025.09.20 |
Spring 기본 시작 - 세팅부터 CRUD API 까지 (2) | 2025.09.04 |
Java 17 다운로드 방법 (0) | 2025.09.03 |