코딩공부/자바

자바의 정석(8) - 자바의 여덟 번째 이야기, 객체지향(4)

에반셀린 2023. 2. 22. 22:31

*이 글은 개인적인 공부/기록 목적으로 올립니다.

부족하다고 느껴질 수 있는 글이니 양해 부탁드립니다.*

 

참고도서

http://www.yes24.com/Product/Goods/24259565

 

Java의 정석 - YES24

최근 7년동안 자바 분야의 베스트 셀러 1위를 지켜온 `자바의 정석`의 최신판. 저자가 카페에서 12년간 직접 독자들에게 답변을 해오면서 초보자가 어려워하는 부분을 잘 파악하고 쓴 책. 뿐만 아

www.yes24.com


상속

 상속이란 클래스를 정의할 때 기존의 클래스를 불러와 이용하는 것을 말한다.상속을 이용하여 클래스를 정의하는 방법은 아주 쉽다.

 

 새로 정의할 클래스 이름 뒤에 extends를 써주고 상속으로 쓸 클래스를 적어주면 된다.그러면 두 클래스는 서로 부모와 자식의 관계를 맺어지게 되고,자식은 부모의 모든 멤버 변수와 메서드를 이용할 수 있게 된다.(생성자, 초기화블럭 제외)

 

 하지만 자식은 부모의 모든 것을 그대로 상속받게 되는 것이지,

원하는 것만 골라서 상속을 받을 수 없다. 그래서 자식의 멤버 개수는

부모의 멤버 변수보다 적을 수 밖에 없다.

// 부모 클래스
class Parent {
	int a;
	int b;
}

// 자식 클래스
class Child extends Parent {
	// ...
}

클래스간의 관계 - 상속관계

class Parent {}
class Child1 extends Parent {}
class Child2 extends Parent {}
class GrandChild extends Child1 {}

 상속 관계를 알게되면 머릿속에 그림이 그려지는데,

상속 관계는 부모 - 자식간의 관계만 존재하지, 형제와 같은 관계는 없다.

위의 코드를 보게 되면 Child1과 Child2는 공통 부모 클래스를 상속받고 있지만

이 두 클래스는 아무런 관계가 없다.

 

또한 GrandChild 클래스는 위의 Child1 클래스에 멤버 변수가 추가 되었다면,

GrandChild 클래스만 영향을 받게 된다.


 클래스간의 관계 - 포함관계

 포함 관계는 한 클래스의 멤버 변수로 다른 클래스를 선언하는 것인데

작은 단위의 클래스를 먼저 만들고, 작은 단위들을 조합해서

하나의 커다란 클래스를 만드는 것을 포함 관계라고 한다.

 

 포함 관계는 나중에 클래스가 복잡할 수록 관련된 멤버들을 묶어서

여러 개의 작은 클래스로 정의하고 이들을 포함하는 것이

코드를 더 간결하게 하고 이해하기 쉽게 만들어 준다. 그 말의 즉슨,

유지보수에 효과적이라는 말일 수 있다.

// 포함 관계 x
class Circle {
	int x;
	int y;
	int r;
}

// 포함 관계 o
class Point {
	int x;
	int y;
}

class Circle {
	Point c = new Point();
	int r;
}

 또한, 상속은 단 하나의 부모만 상속받을 수 있는데 그것을 단일 상속이라고 한다.

예를 들어, 아버지와 어머니라는 부모 클래스가 있는데 자식 클래스가 두 부모 클래스를

동시에 상속을 할 수 없다는 뜻이다. 아버지나 어머니 둘중 하나만 상속이 가능하기 때문에

어디에 상속을 하는 것이 자식 클래스한테 좋은지 생각해볼 필요가 있다.

 

 부모 클래스를 얘기해서 나온 김에 Object라는 클래스가 있다.

Object 클래스는 모든 클래스의 최고 부모 클래스이다.

보통 클래스간의 상속 관계를 타나낼 때 최고 부모 클래스인 Object 클래스를 생략하곤 하지만

실제로는 모든 상속 계층도의 맨 위에 Object가 위치하고 있다.

 

 Object 클래스는 11개의 메서드를 사용할 수 있는데

대표적인 메서드는 toString(), equals(), hashCode()같은 메서드가 있다.


 오버라이딩

 오버라이딩은 부모 클래스로부터 상속받은 메서드의 내용을

상속받는 클래스에 맞게 변경하는 것을 오버라딩이라고 한다.

부모로부터 상속받은 메서드를 그대로 사용하기도 하지만

경우에 따라서는 자식 클래스에 맞게 내용을 변경해야 할 때가 있다.

그럴 때, 오버라이딩이 필요한 것이다.

class Point {
	int x;
	int y;
    
    String getLocation() {
    	return "x : " + x + ", y : " + y;
    }
}

class Point3D extends Point {
	int z;
	String getLocation() {  // 오버라이딩
    	return "x : " + x + ", y : " + y + ". : " + z;
    }
}

 위의 코드를 보면 Point라는 클래스가 있고

Point3D라는 클래스는 Point 클래스를 상속받고 있다.

 

Point3D 클래스를 보면 Point 클래스에 있던 메서드가 존재하는데

차이점은 반환하는 값이 int타입의 z 변수를 추가하였다.

 

이것이 바로 오버라이딩이다.

그런데 아무렇게 오버라이딩을 쓰는 것이 아니다.

오버라이딩에도 조건이 존재하는데 3가지를 들 수 있다.

  1. 선언부가 같아야 한다.(이름, 매개변수, 리턴타입)
  2. 접근제어자를 좁은 범위로 변경할 수 없다.
    - 부모의 메서드가 protected라면, 범위가 같거나
    넓은 protected나 public으로만 변경할 수 있다.
  3. 부모 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.

 

 다른 얘기를 하자면, 많은 사람들이 오버로딩과 오버라이딩을 헷갈려 하는 경향이 있다.

나 또한, 많이 헷갈려 하기에 이번 기회에 공부하면서 개념을 꽉 잡고 가려고 한다.

간단하게 얘기하자면,

  • 오버로딩(overloading) - 기존에 없는 새로운 메서드를 정의하는 것(new)
  • 오버라이딩(orverriding) - 상속받은 메서드의 내용을 변경하는 것(change, modify)
class Parent {
	void parentMethod() {}
}

class Child extends Parent {
	void parentMethod() {}          // 오버라이딩
	void parentMethod(int i) {}  // 오버로딩
}

super

 참조 변수 super는 이전에 참조 변수 this를 배운것과 비슷하다고 볼 수 있다.

this는 지역 변수와 멤버 변수를 구별하는 데 사용한 것처럼super는 상속받은 멤버와 자신의 멤버를 구별하는 데 사용된다.

그리고 부모의 메서드를 참조할 때도 super를 사용할 수 있다.

 

super 말고도 super()가 있는데 자식 클래스의 인스턴스를 생성하면,

자식의 멤버와 부모의 멤버가 합쳐진 하나의 인스턴스가 생성된다.

또 부모의 멤버들도 초기화 되어야 하기 때문에 자식의 생성자의 첫 문장에서

조상의 생성자를 호출해야 한다.