Object 클래스란?
모든 클래스는 Object 클래스의 자손 클래스이다.
public class SomeObject {
}
public class SombeObject extends Object {
}
따라서, extends Object 가 생략되어 있는거라고 생각하면 된다.
Object 클래스의 주요 메서드들
clone(), equals(), finalize(), getClass(), hashCode(), notify(), notifyAll(), toString(), wait()
이 중에서도 특히 자주 사용하는 세 가지 메서드를 알아보려한다
-> equals(), hashCode(), toString()
equals()
- 동일성 : 비교 대상이 실제로 '똑같은' 대상이어야 함 ( = 둘은 실제로는 하나임)
- 두 비교 대상을 '==' 연산자로 비교
- 동등성 : 비교 대상이 같은 값이라고 우리가 정의한 것
- equal() 메서드를 오버라이딩하여 비교
public class SomeObject {
private int intField;
private String stringField;
public SomeObject(int intField, String stringField) {
this.intField = intField;
this.stringField = stringField;
}
public int getIntField() {
return intField;
}
public String getStringField() {
return stringField;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SomeObject that = (SomeObject) o;
return intField == that.intField && Objects.equals(stringField, that.stringField);
}
}
override 한 equals() 는 인텔리제이에서 제공하는 Generate 옵션으로 만든 것이다.
여기서 무심코 썼던 equals() 의 상세한 동작 원리를 알 수 있겠다.
int 필드 하나와, Stirng 필드 하나가 있는 두 Object 가 완전히 같은 인스턴스인지 판단하고 있다.
public class EqualsExampleMain {
public static void main(String[] args) {
SomeObject sameObject1 = new SomeObject(1, "programmers");
SomeObject sameObject2 = new SomeObject(1, "programmers");
SomeObject anotherObject = new SomeObject(100, "foo");
// 동일성 비교 -> false
System.out.println(sameObject1 == sameObject2);
// 동등성 비교 -> true
System.out.println(sameObject1.equals(sameObject2));
// 동등성 비교 -> false
System.out.println(anotherObject.equals(sameObject1));
}
}
출력 중에서 처음 두 줄은
실제로 똑같지는 않지만, 값이 같은 두 Object 인스턴스를 동일성, 동등성에서 비교하는 예제이다.
마지막 출력 줄은
실제로도 다르며, 값도 다른 두 Object 인스턴스를 동등성에서 비교하는 예제이다.
equals() 를 자주 사용하던 String 두 객체를 비교할때는, override 없이 그대로 사용하면 됐다.
허나 지금처럼 필드가 두 개 존재하는 class 를 새로 만들어서 두 객체를 비교할 때는 override 가 필요하다 !
-> 마치 Comparable interface 를 상속받아서 정렬을 위한 우선순위를 만들어주는 것처럼 (아래 예제)
// Comparable 인터페이스를 구현하는 Person 클래스
class Person implements Comparable<Person> {
publice String name;
public Person(String name) {
this.name = name;
}
// compareTo 메서드를 구현하여 이름을 기준으로 오름차순 정렬
@Override
public int compareTo(Person otherPerson) {
return this.name.compareTo(otherPerson.name);
}
@Override
public String toString() {
return name;
}
}
hashCode()
equals() 와 마찬가지로 두 인스턴스가 같다는것을 비교하기위해 override 해서 쓴다고 한다.
다만 차이점은, 이 hashCdoe() 는 두 인스턴스가 hashmap 일 때 쓴다고한다.
작은 수(예를 들면 16)로 나머지한 값을 key 값을 만들기 때문에 key 값이 얼마든지 중복될 수 있다.
따라서 hashCode() 로 같은지 1차 선별을 한 후,
equals() 로 같은지 2차 선별을 하는 것이다.
그러면 처음부터 equals() 로 비교하면 될 텐데, 왜 hashCode() 로 먼저 비교하나?
-> 그 이유는 equals() 보다 상대적으로 가벼운 연산의 hahCode()로 1차 선별을 해서 찾으려는 값의 후보군을 대폭 줄일 수 있다 !
public class SomeObject {
private int intField;
private String stringField;
public SomeObject(int intField, String stringField) {
this.intField = intField;
this.stringField = stringField;
}
public int getIntField() {
return intField;
}
public String getStringField() {
return stringField;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SomeObject that = (SomeObject) o;
return intField == that.intField && Objects.equals(stringField, that.stringField);
}
// hashCode가 오버라이딩 되고 안되고의 차이
// @Override
// public int hashCode() {
// return Objects.hash(intField, stringField);
// }
}
public class HashCodeExampleMain {
public static void main(String[] args) {
// Hash 관련 컬렉션이 같은 인스턴스를 구분하는 방법
// hashCode 비교 -> equals 비교
SomeObject sameObject1 = new SomeObject(1, "programmers");
SomeObject sameObject2 = new SomeObject(1, "programmers");
System.out.println(sameObject1.hashCode());
System.out.println(sameObject2.hashCode());
Set<SomeObject> set = new HashSet<>();
set.add(sameObject1);
set.add(sameObject2);
System.out.println(set.size());
}
}
hashCode() override 한 것을 주석처리하고 main 을 실행하면
sameObject1 과 sameObeject2 각각은 다른 hashCode() 값이 나오게 된다.
따라서 이 둘은 다른 인스턴스로 판별되어서 set.size() 도 2가 된다!
// @Override
// public boolean equals(Object o) {
// if (this == o) return true;
// if (o == null || getClass() != o.getClass()) return false;
// SomeObject that = (SomeObject) o;
// return intField == that.intField && Objects.equals(stringField, that.stringField);
// }
@Override
public int hashCode() {
return Objects.hash(intField, stringField);
}
여기서 만약 hashCode() 는 주석해제하고, equals() 를 주석처리하면?
sameObject1 과 sameObeject2 는 같은 hashCode() 값이 나오지만 equals() 에서 다른 값이 나와서 같지 않다고 판별된다.
따라서 이 경우에도, 둘은 다른 인스턴스로 판별되어서 set.size() 도 2가 된다!
toString()
: 인스턴스를 문자열로 만드는 것 (log 를 남기기 위해)
public class SomeObject {
private int intField;
private String stringField;
public SomeObject(int intField, String stringField) {
this.intField = intField;
this.stringField = stringField;
}
@Override
public String toString() {
return "SomeObject{" +
"intField=" + intField +
", stringField='" + stringField + '\'' +
'}';
}
}
public class ToStringExampleMain {
public static void main(String[] args) {
SomeObject someObject1 = new SomeObject(1, "programmers");
SomeObject someObject2 = new SomeObject(100, "foo");
System.out.println(someObject1);
System.out.println(someObject2);
}
}
// 출력 결과
// SomeObject{intField=1, stringField='programmers'}
// SomeObject{intField=100, stringField='foo'}
toString() 은 상대적으로 간단한 메서드라서
'log 를 찍을 때 사용하고, 출력 형식을 마음대로 만들 수 있다 ~ ' 이정도로 이해하고 넘어가면 되겠습니다.
'백엔드 데브코스' 카테고리의 다른 글
[Java] if문 제거하기 - 리팩토링 (0) | 2023.09.25 |
---|---|
[Java] Optional - nullPointException을 잘 다뤄보자 (0) | 2023.09.25 |
[Java] Checked & Unchecked exception 의 차이 (0) | 2023.09.22 |
[Java] Enum 이란 (0) | 2023.09.22 |
[Java] 추상클래스(abstract class)와 인터페이스(interface) (0) | 2023.09.22 |