Java Category/Java

[JAVA] Object 클래스(euqals(), hashCode(), toString())

ReBugs 2023. 1. 17.

본 게시글은 혼자 공부하는 자바 (저자 : 신용권)의 책과 유튜브 영상을 참고하였고, 개인적으로 정리하는 글임을 알립니다.


클래스를 선언할 때 extends 키워드로 다른 클래스를 상속하지 않더라도 암시적으로 java.lang.Object 클래스를 상속하게 된다.

따라서 자바의 모든 클래스는 Object 클래스의 자식이거나 자손 클래스이다. Object는 자바의 최상위 부모 클래스에 해당한다.

Java SE17 & JDK 17 API Document

Object 클래스는 여러 메소드가 정의되어 있는데, 그중에서 euqals(), hashCode(), toString() 메소드만 살펴보겠다.


객체 비교(equals())

아래의 코드는 Object 클래스의 equals()메소드이다.

public boolean equals(Object obj) {...}

매개타입이 Object인데, 이것은 모든 객체가 매개값으로 대입될 수 있음을 말한다. 왜냐면 모든 객체는 Object 타입으로 자동 타입 변환될 수 있기 때문이다.

Object 클래스의 equals() 메소드는 비교 연산자인 ==과 동일한 결과를 리턴한다. 두 객체가 동일 객체라면 true를 리턴하고 그렇지 않으면 false를 리턴한다.

public class Test{
	public static void main(String[] args) {
		Object obj1 = new Object();
		Object obj2 = new Object();
		System.out.println(obj1.equals(obj2));
		System.out.println(obj1 == obj2);
	}
}
/*
false
false
*/
참조 변수의 ==, != 연산
참조 타입 변수들 간의 ==, != 연산은 동일한 객체를 참조하는지, 다른 객체를 참조하는지 알아볼 때 사용됨
참조 타입 변수의 값은 힙 영역의 객체 주소이므로 ==, != 연산은 결국 주소 값을 비교하는 것이 됨
동일한 주소값을 갖고 있다는 것은 동일한 객체를 참조한다는 의미
반대로 다른 주소값을 갖고 있다는 것은 다른 객체를 참조한다는 의미

 

자바에서는 두 객체를 동등 비교할 때 equals() 메소드를 흔히 사용한다.

동등 객체
같은 타입의 객체일지라도, 두 객체의 필드 값이 모두 같으면 동등 객체, 다르면 다른 객체인지 비교하는 것이다.

equals()메소드는 두 객체를 비교해서 논리적으로 동등하면 true를 리턴하고, 그렇지 않으면 false를 리턴한다.

논리적으로 동등하다
같은 객체이건, 다른 객체이건 상관없이 객체가 저장하고 있는 데이터가 동일함을 뜻함.

예를 들어 String 객체의 equals() 메소드는 String 객체의 주소를 비교하는 것이 아니고, 문자열이 동일한지 조사해서 같다면 true를 리턴하고, 그렇지 않다면 false를 리턴한다.

이것이 가능한 이유는 String 클래스가 Object의 equals()메소드를 오버라이딩해서 주소 비교가 아닌 문자열 비교로 변경했기 때문이다.

 

아래의 코드는 Object의 equals()메소드를 재정의한 것이다.

public class Member {
	public String id;
	public Member(String id) {
		this.id = id;
	}
	@Override
	public boolean equals(Object obj) { //오버라이딩
		if(obj instanceof Member) { //매개값이 Member타입인지 확인
			Member member = (Member) obj; //Member 타입으로 강제 타입변환(모든 타입의 객체가 들어올 수 있기 떄문)
			if(id.equals(member.id)) { // id필드값이 동일한지 검사
				return true;
			}
		}
		return false;
	}
}
public class Test{
	public static void main(String[] args) {
		Member obj1 = new Member("A");
		Member obj2 = new Member("A");
		Member obj3 = new Member("B");
		
		if(obj1.equals(obj2)) {
			System.out.println("obj1 = obj2");
		} else {
			System.out.println("obj1 != obj2");
		}
		
		if(obj1.equals(obj3)) {
			System.out.println("obj1 = obj3");
		} else {
			System.out.println("obj1 != obj2");
		}
	}
}
/*
obj1 = obj2
obj1 != obj2
*/

 

 

객체 해시코드(hashCode())

객체 해시코드란 객체를 식별하는 하나의 정수값을 말한다.

  • Object 클래스의 hashCode() 메소드는 객체의 메모리 주소를 이용해서 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가지고 있다.
  • equals() 메소드와 마찬가지로 hashcode() 메소드 역시 객체의 데이터를 기준으로 오버라이딩해서 새로운 정수값을 리턴하도록 하는 것이 일반적이다.
  • 자바는 두 객체가 동등함을 비교할 때 hashCode()와 equals() 메소드를 같이 사용하는 경우가 많다.
    우선 hashCode()가 리턴하는 정수값이 같은지를 확인하고, 그 다음 equals()메소드가 true를 리턴하는지를 확인해서 동등 객체임을 판단한다.

 

hashCode()와 equals()를 오버라이딩 하여 동등비교

hashCode()나 equals()를 둘 중에 하나라도 오버라이딩 하지 않으면, HashSet에서 동등한 객체인데 다른 객체로 인식한다.
따라서 그냥 단순 동등 비교는 둘 중에 하나만 오버라이딩 하면되지만, HashSet에서까지 동등 객체인지 아닌지 판별하려면 둘 다 오버라이딩 해야한다.

오버라이딩 내용은 다음과 같다.

  1. hashCode()메소드를 같은 타입의 객체이면 같은 값을 리턴하도록 오버라이딩한다.
  2. equals()메소드를 필드 값의 내용이 같으면 동등 객체라고 판단하도록 오버라이딩한다.

 

Student.java

public class Student {
	private int no;
	private String name;

	public Student(int no, String name) {
		this.no = no;
		this.name = name;
	}

	public int getNo() { return no; }
	public String getName() { return name; }

	@Override
	public int hashCode() { //오버라이딩
		int hashCode = no + name.hashCode();
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) { //오버라이딩
		if(obj instanceof Student target) {
			if(no == target.getNo() && name.equals(target.getName())) {
				return true;
			}
		}
		return false;
	}
}

 

Main.java

public class HashCodeExample {
	public static void main(String[] args) {
		Student s1 = new Student(1, "홍길동");
		Student s2 = new Student(1, "홍길동");
		Student s3 = new Student(1, "홍길순");

		if(s1.hashCode() == s2.hashCode()) {
			if(s1.equals(s2)) {
				System.out.println("동등 객체입니다.");
			} else {
				System.out.println("데이터가 다르므로 동등 객체가 아닙니다.");
			}
		} else {
			System.out.println("해시코드가 다르므로 동등 객체가 아닙니다.");
		}
        
		if(s2.hashCode() == s3.hashCode()) {
			if(s1.equals(s2)) {
				System.out.println("동등 객체입니다.");
			} else {
				System.out.println("데이터가 다르므로 동등 객체가 아닙니다.");
			}
		} else {
			System.out.println("해시코드가 다르므로 동등 객체가 아닙니다.");
		}
	}
}
/*
동등 객체입니다.
해시코드가 다르므로 동등 객체가 아닙니다.
*/

 

아래는 HashSet에서 동등 객체인지 아닌지 판별하는 예제이다.

HashSetExample.java

import java.util.HashSet;
public class HashSetExample {
	public static void main(String[] args) {
		HashSet hashSet = new HashSet();

		Student s1 = new Student(1, "홍길동");
		hashSet.add(s1);
		System.out.println("저장된 객체 수: " + hashSet.size());

		Student s2 = new Student(1, "홍길동");
		hashSet.add(s2);
		System.out.println("저장된 객체 수: " + hashSet.size());

		Student s3 = new Student(2, "홍길동");
		hashSet.add(s3);
		System.out.println("저장된 객체 수: " + hashSet.size());
	}
}
/*
저장된 객체 수: 1
저장된 객체 수: 1
저장된 객체 수: 2
*/

위 예제에서 보는 것과 같이 동등한 객체는 카운트하지 않는 것을 볼 수 있다.

 

 

 


객체 문자정보 (toString())

Object 클래스의 toString() 메소드는 객체의 문자 정보를 리턴한다. 객체의 문자 정보란 객체를 문자열로 표현한 값을 말한다. 기본적으로 Object 클래스의 toString()메소드는 '클래스이름@16진수해시코드'로 구성된 문자 정보를 리턴한다.

java.lang.Object@372f7a8d 이런 식으로 객체의 문자 정보를 리턴한다.

이러한 리턴 값은 별 값어치가 없는 정보이므로 Object 하위 클래스는 toString()메소드를 오버라이딩하여 유익한 정보를 리턴하도록 되어있다.

예를 들어 java.util 패키지의 Date클래스는 toString() 메소드를 재정의하여 저장하고 있는 문자열을 리턴한다.

import java.util.Date;

public class Test{
	public static void main(String[] args) {
		Object obj1 = new Object();
		Date obj2 = new Date();
		System.out.println(obj1.toString());
		System.out.println(obj2.toString());
	}
}
/*
java.lang.Object@372f7a8d
Thu Jan 12 23:45:57 KST 2023
*/

 java.util 패키지의 Date클래스는 toString() 메소드를 객체의 생성시간을 리턴하도록 되어있다.

 

toString() 메소드를 오버라이딩 하는 방법은 간단하다

SmartPhone.java

public class SmartPhone {
	private String company;
	private String os;

	public SmartPhone(String company, String os) {
		this.company = company;
		this.os = os;
	}

	@Override
	public String toString() { //오버라이딩
		return company + ", " + os;
	}
}

 

Main.java

public class Main {
	public static void main(String[] args) {
		SmartPhone myPhone = new SmartPhone("삼성전자", "안드로이드");

		String strObj = myPhone.toString(); //재정의 한 메소드 값을 저장
		System.out.println(strObj); //저장한 값 출력

		System.out.println(myPhone); //그냥 객체 자신을 출력하면 toString의 값 출력
	}
}
/*
삼성전자, 안드로이드
삼성전자, 안드로이드
*/

 

댓글