Object 메소드 정리 - equals(), hashCode(), toString(), thin clone()과 deep clone()
지금껏 Object클래스의 메소드를 사용하면서 깊게 생각안하고, 편하게 사용만한것이 반성이된다.
- 최상위 부모 클래스 Object 클래스 - java.lang의 클래스
Object 클래스는 말그대로 최상위 부모 클래스이다. 명시적으로 extends키워드로 다른 클래스를 상속하지 않는 한, java.lang.Object클래스를 상속한다. 즉, 자바의 모든 클래스는 Object 클래스의 자식이거나 자손 클래스이다.Object 클래스는 필드가 없고, 메소드로만 구성되어있다. 모든 클래스는 Object 클래스를 상속하므로 모든 클래스에서 이용이 가능하다. - 객체비교 (equals())
public boolean equals(Object obj) {...}
equals()메소드의 매개타입은 object이다. 이말은 모든 객체가 매개값으로 대입가능하다는 것이다.
= Object가 최상위 타입으로 모든 객채가 Object타입으로 자동 타입변환 될 수 있다는 의미이다..
equals()메소드는 비교연산자 ==와 동일한 결과를 리턴한다. 두 객체가 논리적으로 동등한 동일한 객체면 true, 아니면 false를 리턴한다. 논리적으로 동등하다는 것은 같은 객체이건 다른 객체이건 상관없이 객체가 저장하고 있는 데이터가 동일함을 뜻한다.
여기서 다른객체도 상관없다는 것은 다른 타입도 상관없다는 것이 아니다.
equals()메소드는 직접사용되지 않고 하위 클래스에서 재정의하여 논리적으로 동등비교 할때 사용한다.
비교객체와 기준객체의 타입은 동일해야한다. 즉, 타입이 동일한 객체인지 먼저 확인해야한다.
Object타입의 매개변수는 모든 객체가 매개값으로 가능하기때문에, instanceof 연산자로 기준객체와 동일한 타입인지 제일 먼저 확인해야 한다.
만약 비교객체가 다른타입이면 equals() 메소드는 false를 리턴하고, 동일한 타입이면 기준객체 타입으로 강제타입변환(casting)하여 필드값이 동일한지 검사하면 된다.
- 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; //Mmember 타입으로 강제타입변환하고
if(id.equals(member.id)) { //id필드값이 동일한지 검사한 후,
return true; //동일하다면 true를 리턴한다.
}
}
return false; //매개값이 Member타입이 아니거나 id필드값이 다른경우
} //false를 리턴한다.
}
- 객체 해시코드 hashCode()
객체 해시코드란 객체를 식별할 하나의 정수값이다. 해시코드 메소드는 객체의 메소드 번지를 이용해서 해시코드를 만들어 리턴하기 때문에 객체마다 다른값을 가지고 있다. -->>컬렉션 프레임워크에서 더 깊이 다시 다루어야겠다.
우선 hashCode() 메소드를 실행해서 리턴된 해시코드 값이 같은지 확인하다.
해시코드 값이 다르면 다른객체로 판단하고, 해시코드 값이 같으면 equals()메소드로 다시 비교한다.
그렇게 때문에 hashCode()메소드가 true여도 equals()의 리턴값이 다르면 다른객체이다.
즉, 객체의 동등비교를 하려면 Object의 equals()메소드만 재정의가 필요한게 아니라, hashCode() 메소드도 재정의해서 논리적 동등 객체일 경우 동일한 해시코드가 리턴되도록 해야한다. - eqauls()메소드를 설명할때 사용한 member클래스 코드에 hashCode()메소드 재정의를 추가했다.
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; //Mmember 타입으로 강제타입변환하고
if(id.equals(member.id)) { //id필드값이 동일한지 검사한 후,
return true; //동일하다면 true를 리턴한다.
}
}
return false; //매개값이 Member타입이 아니거나 id필드값이 다른경우
}
@Override
public int hashCode() {
return id.hashCode(); //id가 동일한 문자열인 경우 같은 해시코드를 리턴한다!**
}
}
- toString() - 객체의 문자정보를 리턴하는 메소드
말그대로 toString()메소드는 객체의 문자정보를 리턴한다. 객체의 문자정보란 객체를 문자열로 표현한 값이다.
Object obj = new Object();
System.out.println( obj.toString() );
의 실행결과는 java .lang .Object@de6ced
이런 정보를 리턴하는데, 별 값어치가 없는 정보이므로 하위클래스에서toString()메소드를 오버라이딩(재정의)
하여 리턴하도록 한다.
예를 들어, java.util패키지의 Date클래스는 toString()메소드를 오버라이딩해서 현재 시스템의 날짜, 시간정보를 리턴하고
String클래스에서는 toString()메소드를 오버라이딩하여 저장하고 있는 문자열을 리턴한다.(평소에 toString메소드를 많이 사용하는데 오버라이딩하여 사용하는것 조차 모르고있었다......)
- 객체복제 clone()
객체복제는 원본객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것이다.
애초에 객체복제를 하는 이유는, 원본객체를 훼손하지 않기 위해서이다. 원본객체를 넘겨 원본객체에서 작업하게 되면 위험하기때문에, 원본객체는 놔두고 복제객체를 만들어 작업하는 것이다.
복제에는 얕은복제와, 깊은복제가 있다. - 얕은 복제 thin clone()
단순히 필드값을 복사해서 객체를 복제하는 것이다. 때문에, 필드가 기본타입이면 값복사, 참조타입이면 객체의 번지주소가 복사된다. Object의 clone()메소드는 자신과 동일한 필드값을 가진 얕은 복제 객체를 리턴한다.
이 메소드로 객체를 복제하려면 원본객체는 무조건 java.lang.Cloneable 인터페이스를 구현하고 있어야하며,
메소드 선언이 없을때에서 Cloneable인터페이스를 명시적으로 구현하는 이유는 클래스 복제를 허용한다는 표시이다.
Cloneable인터페이스를 구현하지 않고서 clone()메소드를 호출하면 예외(CloneNotSupportedException)가 발생한다.
즉, clone()메소드는 예외처리가 필요한 메소드로서 try-catch 코드가 반드시 필요하다!!
try {
Object obj = clone();
}catch(CloneNotSupportedException e) { }
public class Member implements Clone { //복제 허용 표시
....
public Member getMember() {
Member cloned = null;
try {
cloned = (Member)clone();
}catch (CloneNotSupportedException e) {}
return cloned;
}
...
위getMember()메소드에서 clone메소드의 리턴타입이 Object이므로 Member타입으로 캐스팅해준 것이다.
깊은복제 deep clone()
얕은 복제의 경우 참조타입 필드는 번지만 복제되어 원본객체 필드와 복제객체의 필드는 같은 객체를 참조하게 된다. 이말은 복재객체에서 참조객체를 변경하면 원본객체도 변경된 객체를 가지게 된다는 것이다..
그래서 깊은 복제가 필요한데, 깊은 복제는 참조하고 있는 객체까지도 복제한다는 것을 말한다.
깊은 복제를 하려면 clone() 메소드를 재정의해서 참조객체를 복제하는 코드를 직접 작성해야한다.
public String name;
public int age;
public int[] scores; //깊은복제 대상
public Car car;//깊은복제 대상
이 둘은 모두 참조타입 필드로 깊은복제 대상이다.
....
@Override
protected Object clone() throws CloneNotSurpportedException {
//먼저 얕은 복사를 해서 name, age를 복제한다.
Member cloned = (Member)super.clone(); //Object의 clone()호출;
//scores, car 깊은복제
cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
cloned.car = Arrays.copyOf(this.car.model);
//깊게 복제된 Member객체를 리턴한다
return cloned;
}
.....
//재정의된 clone을 호출한다.
public Member getMember() {
Member cloned = null;
try {
cloned = (Member)clone();
}catch (CloneNotSupportedException e) {}
return cloned;
}
getMember()메소드로 호출해서 복제된 Member객체를 얻은 후 scores배열항복과, car객체의 모델항목을 변경한다. 여기서 원본 Member객체의 scores, car 객체 모델은 변함이 없다.
그 이유는, 원본과 복제본이 각각 참조하는 객체는 서로 다르기 때문이다!!
( = 깊은 복제는 복제본의 변경이 원본에 영향을 미치지 않는다.)
'Language > JAVA' 카테고리의 다른 글
StringTokenizer 클래스 (0) | 2023.07.07 |
---|---|
API_3. String 클래스 (0) | 2023.07.06 |
API_2. java.util패키지의 Objects 클래스 (0) | 2023.07.06 |
JAVA API 클래스 - 1 (0) | 2023.07.06 |
MVC 모델 2 게시판 만들기 - 1 (0) | 2022.11.01 |