▸JAVA/기본 문법

객체의 등가성(객체 비교)_equals 메소드 [3/4]

코데방 2019. 12. 10.
728x90

[ equals 메소드 ]

  • 객체 내부의 값을 모두 비교해 동일 값을 가진 객체를 판별하는 메소드

equals 메소드는 모든 클래스의 기본적인 부모 클래스가 되는 Object 클래스 포함된 메소드입니다. 하지만 기본 메소드를 그대로 사용할 경우 올바른 비교가 되지 않기 때문에 실제 사용 시에는 해당 클래스에 맞도록 오버라이딩 해서 사용해줍니다. 물론 오버라이딩 하지 않고 다른 함수를 만들어 사용해도 무방합니다만, 혼선을 방지하기 위해 그냥 오버라이딩을 해주는 쪽이 좋습니다.

 


 

먼저 Object에 포함된 기본 equals 메소드의 코드를 보면 객체 자체를 관계 연산자(==)로 비교합니다. 따라서 내부적으로 같은 값을 가진 객체 2개라도 다른 메모리 주소를 가지기 때문에 서로 다른 값으로 인식합니다. 자기 자신을 비교하지 않는 이상 무조건 다른 값이 되는 것이죠. 사용할 일은 그다지 없을 것 같습니다.

package study.first;

public class Main {

	public static void main(String[] args) {

		Sub s1 = new Sub();
		Sub s2 = new Sub();
	
		System.out.println(s1.equals(s1));	// true
		System.out.println(s1.equals(s2));	// false		
	}	
}

class Sub {
	
	int x;
	String a;
	
}

 


 

 

기본적인 메소드를 만드는 것은 쉽습니다. 그냥 해당 클래스의 변수가 가진 필드 값끼리 같은지 관계 연산자(==)로 비교해주면 됩니다.

parameter로 "Object 타입"를 사용하는 이유는 기본 Object.equals 메소드의 매개변수가 Object 타입이라 오버라이딩을 하기 위해 똑같이 맞춰주기 위함입니다. 만약 다른 타입으로 매개변수를 받으면 오버로딩으로 인식해서 기본 equals 메소드가 그대로 남아있게 되어 함수 호출 시 넣는 인자에 따라 다른 결과가 도출될 수 있는 위험이 존재합니다. 하지만 조금 다른 방식으로 구현하고 이름도 다르게 하고 싶다면 굳이 Object 타입을 사용하지 않아도 작동합니다. 다만 다른 타입의 객체를 인자값으로 넣을 경우 Warning이 뜨기 때문에 그냥 Object 타입을 사용하는 것을 권장합니다.

package study.first;

public class Main {

	public static void main(String[] args) {

		Sub s1 = new Sub(1, "abc");
		Sub s2 = new Sub(1, "abc");

		System.out.println(s1.equals(s2));  // true					
	}	
}


/* Sub 클래스 */
class Sub {
	
	int x;
	String a;	
	
	/* 생성자 선언 */
	public Sub(int x, String a) {
		this.x = x;
		this.a = a;
	}	

	public boolean equals(Object obj) {
		
		Sub temp = (Sub) obj;
		if (temp.x == this.x && temp.a == this.a)
			return true;
		else return false;		
	}	
}

 


 

위와 같이 생성자나 직접 코드에서 값을 넣어줄 경우 같은 문자열은 같은 주소를 가리키게 되기 떄문에 true가 됩니다. 하지만 사용자가 직접 입력해 주거나 서로 다른 객체로서의 문자열을 가리키고 있을 때는 값이 같더라도 저장된 메모리 주소가 다르기 때문에 false가 반환됩니다.즉, 위와 같은 코드로는 정확한 비교를 하기 힘들어집니다. 필드가 원시타입으로만 구성되어 있다면 값으로 비교해주기 때문에 상관 없습니다. 이부분은 관계 연산자 설명 부분을 참조하시면 됩니다.

 

2019/12/10 - [JAVA/기본 문법] - 객체의 등가성(객체 비교)_관계 연산자(==) 비교 [1/4]

 

 

따라서 보다 정확한 객체 간 비교를 위해 사용하는 것이 이클립스에서 자동 완성해주는 형식의 equals와 hashCode 메소드입니다. 기본적으로 2가지를 동시에 쓰는 것을 권장하지만 단순 비교만 하는 경우라면 equals 메소드 하나만 사용해도 무방합니다. 왜 혼용을 권장하는지는 다음글 hashCode 설명에서 정리하겠습니다. 아래는 이클립스에서 완성시켜준 equals 메소드입니다. 해시코드와 같이 쓰는게 권장이라 두 가지를 동시에 만들어 줍니다.

 

그리 어려운 코드는 아닙니다. 먼저 큰 단위로 객체 자체를 비교한 후, 비교 대상이 맞으면 필드를 비교합니다. 3번 째 if문에서 보듯이 형식과 값이 같아도 클래스가 다르면 다른 것으로 간주합니다. 문자열은 문자열 비교 메소드를 다시 사용해서 비교하고 원시 타입은 그냥 비교해줍니다.

package study.first;

public class Main {

	public static void main(String[] args) {

		Sub s1 = new Sub();
		Sub s2 = new Sub();
		System.out.println(s1.equals(s2));   // True

	}
}

/* Sub 클래스 */
class Sub {

	int x;
	String a;

	@Override
	public boolean equals(Object obj) {
	
		/* 객체 단위 비교 */
		if (this == obj)  // 자기 자신이 비교 대상으로 들어왔으면 true
			return true;
		if (obj == null)  // 메모리 주소가 없는 객체라면 false
			return false;
		if (getClass() != obj.getClass())   // 비교 대상 둘이 다른 클래스라면 false
			return false;  // 필드와 값이 완전히 같더라도 다른 클래스 소속이면 false 

		/* 객체의 내용 비교 */
		Sub other = (Sub) obj;  // Object 타입으로 들어왔으므로 비교를 위해 형변환
		if (a == null) {  // 문자열 비교해서 다르면 false
			if (other.a != null)
				return false;
		} else if (!a.equals(other.a))
			return false;
		if (x != other.x)  // int(원시 타입) 비교해서 다르면 false
			return false;
		return true;  // 모두 같으면 true 리턴
	}
}

 


 

원리만 간단히 알고 그냥 이클립스에서 만들어주는 메소드를 쓰면 될 것 같습니다. 참고로 Source(Alt + Shift + S) 안에서 자동 생성해주는 메소드들은 거의 필수적이라 할만큼 자주 사용되는 표준 권장 사항이라고 보면 됩니다.

728x90

댓글

💲 추천 글