본문 바로가기
Java/Java

상속) 최상위 클래스 Object, toString(), equals(Object obj), hashCode(), clone()

by 박채니 2022. 3. 21.
SMALL

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


최상위 클래스 Object

Object 클래스는 자바의 최상위 클래스로,  자바의 모든 클래스들은 Object 클래스를 상속 받습니다.

 

class Parent {
	String name;
	int age;
	
	public void say() {
		System.out.println("부모클래스");
	}
	
	public String information() {
		return "name = " + name + ", age = " + age;
	}
}

class Child extends Parent {
	String game = "LOL";
	
	public void doGame() {
		System.out.println(name + "가 " + game + "게임을 한다.");
	}
}

class GrandChild extends Child {
	
}

class OtherChild extends Parent{
	
}

Parent 클래스를 상속 받은 Child 클래스

Child 클래스를 상속 받은 GrandChild 클래스

Parent 클래스를 상속 받은 OtherChild 클래스를 생성해보았습니다.

 

그렇다면 부모 클래스인 Parent 클래스는 extends 키워드가 없으므로 아무도 상속 받지 않는 것일까요?

아닙니다. 위에서 말했듯 모든 자바 클래스는 Object 클래스를 상속 받습니다.

즉 Parent 클래스는 Object 클래스를 상속 받고 있는 것이죠.

 

JVM은 아무런 클래스를 상속하지 않으면 자동으로 extends Object를 삽입하여 Object 클래스를 상속합니다.

class Parent extends Object {
	String name;
	int age;
	
	public void say() {
		System.out.println("부모클래스");
	}
	
	public String information() {
		return "name = " + name + ", age = " + age;
	}
}

extends Object가 생략 되어 있었기 때문에 아무런 클래스도 상속 받지 않는다고 생각 들었던 것입니다.

 

그렇다면 위 클래스들은 아래와 같은 상속 구조를 갖고 있겠죠.

 

그렇다면, 모든 클래스들은 Object클래스를 상속 받고 있으므로 Object에서 제공하는 메소드들을 사용할 수 있다는 것이겠죠?

메소드들을 하나씩 살펴보겠습니다. (notify(), notifyAll(), wait()은 쓰레드 시 사용하는 메소드이므로 추후 알아보겠습니다.)


☞ toString()

- 객체 정보를 문자열로 출력

 

먼저 클래스를 하나 만들어보겠습니다.

public class Car {
	private String carName;
	private String color;
	private int doorNum;
	
	public Car() {}
	public Car(String carName, String color, int doorNum) {
		this.carName = carName;
		this.color = color;
		this.doorNum = doorNum;
	}
	
	public void setCarName(String carName) {
		this.carName = carName;
	}
	public void setColor(String color) {
		this.color = color;
	}
	public void setDoorNum(int doorNum) {
		this.doorNum = doorNum;
	}
	public String getCarName() {
		return carName;
	}
	public String getColor() {
		return color;
	}
	public int getDoorNum() {
		return doorNum;
	}
}
public class CarMain {
	public static void main(String[] args) {
		CarMain main = new CarMain();
		System.out.println(main.toString());
	}
}

@콘솔출력값
com.ce.java.CarMain@4361bd48

객체 정보는 '패키지명.클래스명@해시코드'로 나타냅니다.

또한 print메소드들은 객체를 출력하면 자동으로 객체 내의 toString() 메소드를 호출하므로 아래와 같이 호출하여도 동일한 출력값이 나옵니다.

System.out.println(main);

@콘솔출력값
com.ce.java.CarMain@4361bd48

대부분 자식 클래스에서 toString()메소드를 오버라이딩하여 사용합니다.

@Override
	public String toString() {
		return "Car [carName = " + carName + ", color = " + color + ", doorNum = " + doorNum + "개]";
	}
public class CarMain {
	public static void main(String[] args) {
		Car mySonata = new Car("소나타", "펄화이트", 4);
		System.out.println(mySonata);
	}
}

@콘솔출력값
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]

 

☞ equals(Object obj)

- 입력매개변수로 넘어온 객체와 자기 객체의 스택 메모리 변숫값을 비교하여 true 또는 false로 리턴

- 실제 데이터의 값이 아닌 실제 데이터의 위치(번지)를 비교하는 것

public class CarMain {
	public static void main(String[] args) {
		Car mySonata = new Car("소나타", "펄화이트", 4);
		System.out.println(mySonata);
		
		//객체 비교
		Car yourSonata = new Car("소나타", "펄화이트", 4);
		System.out.println(yourSonata);
		
		System.out.println(mySonata == yourSonata);
		System.out.println(mySonata.equals(yourSonata));
	}
}

@콘솔출력값
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]
false
false

equals(Object obj)메소드 또한 스택 메모리의 값을 비교하는 것이므로 == 동등 비교 연산자와 동일한 기능을 수행합니다.

mySonata와 yourSonata는 내용은 같지만 서로 다른 객체의 주소값을 갖고 있으므로 false가 출력됩니다.

 

만일 객체 내용이 같을 때 true를 리턴하고 싶다면 equals(Object obj)메소드를 오버라이딩하여 사용합니다.

@Override
	public boolean equals(Object obj) {
		Car other = (Car)obj;
		if(!(this.carName.equals(other.carName)))
			return false;
		if(!(this.color.equals(other.color)))
			return false;
		if(this.doorNum != other.doorNum)
			return false;
		
		return true;
	}

Object 타입으로 매개변수를 받기 때문에 반드시 동일한 타입으로 다운 캐스팅을 해줘야 하므로,

Car 타입의 obj를 other 참조변수로 받아주었습니다.

 

현재객체의 carName과 매개변수로 넘어온 객체의 carName이 같지 않다면 false를 리턴

현재객체의 color와 매개변수로 넘어온 객체의 color가 같지 않다면 false를 리턴

현재객체의 doorNum과 매개변수로 넘어온 객체의 doorNum이 같지 않다면 false를 리턴

해당 되지 않으면 true를 리턴하게 하여 값이 동일하면 true를 리턴하도록 해주었습니다.

 

여기서 if문 내의 equals는 String에서 제공해주는 메소드로 String의 실제 값을 비교해주는 메소드입니다!

public class CarMain {
	public static void main(String[] args) {
		Car mySonata = new Car("소나타", "펄화이트", 4);
		System.out.println(mySonata);
		
		//객체 비교
		Car yourSonata = new Car("소나타", "펄화이트", 4);
		System.out.println(yourSonata);
		
		System.out.println(mySonata == yourSonata);
		System.out.println(mySonata.equals(yourSonata));
	}
}

@콘솔출력값
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]
false
true

 

☞ hashCode()

- 객체의 위치와 관련된 값

- 실제 위치를 나타내는 값은 아니며, 객체의 위칫값을 기준으로 생성된 고윳값

- equals 비교 결과가 true라면 hashCode의 값 또한 같아야함

 

오버라이딩 전

System.out.println(mySonata.hashCode());
System.out.println(yourSonata.hashCode());

@콘솔출력값
1982791261
1562557367

오버라이딩 하기 전 hashCode를 출력해보면 실제 값은 동일하지만 서로 다른 객체이므로 해시코드 값이 다른 것을 확인할 수 있습니다.

 

오버라이딩 후 

@Override
	public int hashCode() {
		return Objects.hash(carName, color, doorNum);
	}

측정 필드 값(동등성비교에 사용될) 기준으로 hashCode를 재생성하여 리턴해주었습니다.

Objects에서 제공하는 hash()메소드를 활용!

System.out.println(mySonata.hashCode());
System.out.println(yourSonata.hashCode());

@콘솔출력값
64676483
64676483

 

☞ clone()

- 동일한 내용을 가진 객체 복제

 

Object에서 제공하는 clone()메소드는 아래와 같습니다.

@Override
	protected Object clone() {
		
	}

접근제한자는 protected, 리턴 타입은 Object입니다.

따라서 메인 메소드에서 메소드 호출과 캐스팅 문제가 발생하게 됩니다.

//에러발생
Car hisSonata = mySonata.clone();

이를 해결하기 위해 protected를 public으로 변경하고,

공변 반환타입(covariant return type) : 조상 메소드의 반환 타입을 자식 클래스 타입으로 변경하여 메소드를 오버라이딩해보았습니다.

@Override
	public Car clone() {
		return null;
	}

 

복사 생성자를 만들어서 매개인자로 넘겨주어 값을 대입하여 복제를 할 수 있습니다.

//복제 생성자
public Car(Car other) {
		this.carName = other.carName;
		this.color = other.color;
		this.doorNum = other.doorNum;
	}
    
	@Override
	public Car clone() {
		return new Car(this);
	}
public class CarMain {
	public static void main(String[] args) {
		Car mySonata = new Car("소나타", "펄화이트", 4);
		System.out.println(mySonata);
		System.out.println(mySonata.hashCode());
		
		Car hisSonata = mySonata.clone();
		System.out.println(hisSonata);
		System.out.println(hisSonata.equals(mySonata));
		System.out.println(hisSonata.hashCode());
	}
}

@콘솔출력값
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]
64676483
Car [carName = 소나타, color = 펄화이트, doorNum = 4개]
true
64676483

 

LIST