상속
상속이란 한자를 풀어보면 "뒤를 이어준다" 라는 뜻이 된다.
객체지향프로그래밍에 맞춰서 다시 생각해 보면 어떤 객체가 가지고 있던 변수나 메소드를 다른 클래스가 물려받아
사용할 수 있다.
1. 상속의 개념
상속을 사용하기 위해서는 두 개 이상의 클래스가 필요하다.
최소한 물려줄 클래스 1개, 물려준 걸 받을 클래스 1개가 필요한데 이런 관계를 '조상, 후손' 또는 '부모, 자식' 으로
부른다. 이번 포스팅에서는 부모, 자식을 주 명칭으로 사용하고자 한다.
우선 상속을 사용하는 문법은 다음과 같다.
class Point {
int x;
int y;
}
class Point3D extends Point // Point 클래스를 상속받은 Point3D 클래스 선언
{
int z;
}
아래쪽 클래스의 extends 라는 문장을 눈여겨 살펴보자.
extend란 확장하다 라는 뜻으로 사용된다. 즉, Point 클래스를 확장하여 Point3D 클래스를 만들겠다는 뜻이다.
여기서 관계를 살펴보면 아래와 같다.
부모 : Point
자식 : Point3D
위의 소스를 좀 더 확장하여 좀 더 알아보자.
package com.inheritance;
public class InhMain {
public static void main(String[] args) {
//TODO Auto-generated method stub
Point p1 = new Point();
p1.x = 100;
p1.y = 200;
System.out.println("x = " + p1.x);
System.out.println("y = " + p1.y);
Point3D p3d1 = new Point3D();
p3d1.x = 10;
p3d1.y = 20;
p3d1.z = 50;
System.out.println("x = " + p3d1.x);
System.out.println("y = " + p3d1.y);
System.out.println("z = " + p3d1.z);
}
}
class Point {
int x;
int y;
public Point() {
System.out.println("Point 생성자 입니다.");
}
}
class Point3D extends Point // Point 클래스를 상속받은 Point3D 클래스 선언
{
int z;
public Point3D() {
System.out.println("Point3D 생성자 입니다.");
}
}
소스코드 아래쪽에 Point 클래스와 Point3D 클래스가 있다.
그리고 코드 위의 main 쪽을 보면 각 클래스의 인스턴스를 만들어 갑을 입력하는 것을 볼 수 있다.
그런데 Point3D 형 p3d1 인스턴스를 보면 Point3D 의 인스턴스 변수는 z 밖에 없지만 실제 사용된건 x, y, z 를 모두 사용하고 있다. 즉, x, y 를 가진 Point 클래스의 멤버변수를 물려받아 사용하고 있다는 것을 알 수 있다.
아래의 결과를 보면 바로 확인할 수 있다.

그런데 결과를 보면 이상한 점이 있다.
코드에서 만들어진 인스턴스는 [p1, p3d1] 2개인데 Point 클래스의 생성자가 두 번 호출된 것을 확인할 수 있다.
즉, 자식 클래스의 인스턴스가 생성되면 우선 부모의 생성자가 먼저 호출되고 그 다음 자식 생성자가 호출되어
부모 클래스의 변수의 메소드들이 먼저 메모리에 올라가고 그 다음으로 자식 클래스의 변수와 메소드가 메모리에
올라간다는 것을 확인할 수 있다.
2. protected (접근 지정자와 상속)
이번에는 위의 예제를 수정하여 상속으로 어디까지 받을 수 있을까 살펴보려한다.
※ public 으로 선언되면 의미가 없기에 public 은 정리에서 제외한다.
1) 부모 클래스의 맴버 변수가 private
package com.inheritance;
public class InhMain {
public static void main(String[] args) {
//TODO Auto-generated method stub
Point p1 = new Point();
p1.setX(100);
p1.setY(200);
System.out.println("x = " + p1.getX());
System.out.println("y = " + p1.getY());
Point3D p3d1 = new Point3D();
p3d1.setX(10);
p3d1.setY(20);
p3d1.setZ(30);
System.out.println("x = " + p3d1.getX());
System.out.println("y = " + p3d1.getY());
System.out.println("z = " + p3d1.getZ());
}
}
class Point {
private int x;
private int y;
public Point() {
System.out.println("Point 생성자 입니다.");
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
class Point3D extends Point // Point 클래스를 상속받은 Point3D 클래스 선언
{
private int z;
public Point3D() {
System.out.println("Point3D 생성자 입니다.");
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
}
위의 예제를 보면 각 클래스의 멤버변수는 private 로 선언되어 있다. 그리고 main() 에서 getter/setter 메소드로
각 인스턴스 변수에 값을 넣어주는 것을 확인할 수 있다.

실행 결과를 보면 전혀 이상 없이 접근할 수 있는 것을 확인할 수 있다.
즉, 부모 클래스의 멤버 변수를 private 로 선언해도 상속받은 자식 클래스도 사용할 수 있다.
이번에는 자식 클래스에 새로 메소드를 추가하여 보자.
class Point3D extends Point // Point 클래스를 상속받은 Point3D 클래스 선언
{
private int z;
public Point3D() {
System.out.println("Point3D 생성자 입니다.");
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
public void inputData() {
// x = 100; // error!
// y = 100; // erroe!
setX(100);
setY(100);
}
}
inputData() 라는 자식 클래스의 메소드에서 직접 부모의 x와 y 를 사용하는 것은 불가하여 에러가 발생된다.
즉, 부모의 private 변수에 접근하기 위해서는 getter / setter 메소드로 접근해야만 한다.

2) 부모 클래스의 맴버 변수가 protected 로 선언
지난 포스팅에서 private와 public에 대해서는 정리를 하였지만 protected 는 정리하지 않았다.
드디어 상속을 정리하게 되며 protected가 등장하게 되었다.
우선 protected란 말 그대로 보호하다란 의미이다.
protected 는 자신 외에는 무조건 안되는 private와 모두 허용해주는 public 의 중간단계의 성격을 지닌다.
즉, protected는 같은 패키지 내에 있거나 상속받은 경우에만 접근할 수 있다.
package com.inheritance;
public class InhMain {
public static void main(String[] args) {
//TODO Auto-generated method stub
Point p1 = new Point();
p1.setX(100);
p1.setY(200);
System.out.println("x = " + p1.getX());
System.out.println("y = " + p1.getY());
Point3D p3d1 = new Point3D();
p3d1.setX(10);
p3d1.setY(20);
p3d1.setZ(30);
p3d1.x = 10000;
p3d1.y = 20000;
p3d1.z = 30000;
System.out.println("x = " + p3d1.getX());
System.out.println("y = " + p3d1.getY());
System.out.println("z = " + p3d1.getZ());
}
}
class Point {
protected int x;
protected int y;
public Point() {
System.out.println("Point 생성자 입니다.");
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
class Point3D extends Point // Point 클래스를 상속받은 Point3D 클래스 선언
{
int z;
public Point3D() {
System.out.println("Point3D 생성자 입니다.");
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
public void inputData() {
x = 100; // 부모에서 protected로 선언되어 문제없다!
y = 100;
setX(100);
setY(100);
}
}
소스코드에서 보면 우선 자식클래스의 inputData 메소드의 에러가 사라지는 것을 확인할 수 있다.
그 이유는 부모클래스의 멤버 변수가 protected로 선언되었기 때문이다.
또한 main() 에서 보면 클래스 외부에서 값을 직접 접근하여 입력하여도 문제가 없는 것을 확인할 수 있다.
즉, 같은 패키지 내에서는 접근을 허용해 주는 것을 볼 수 있다.
3) static 으로 선언된 변수( 메소드 )
만약 부모 클래스에서 static으로 선언된 클래스 변수와 클래스 메소드도 상속이 가능할까?
이번 소스에서는 메소드 상속 여부와 클래스 변수, 클래스 메소드 상속 여부를 확인해본다.
package com.inheritance;
public class InhMain {
public static void main(String[] args) {
//TODO Auto-generated method stub
Point3D p3d1 = new Point3D();
p3d1.x = 10000;
p3d1.y = 20000;
p3d1.z = 30000;
p3d1.number = 7777; // 부모의 클래스 변수에 값 입력
p3d1.Hi(); // 부모의 클래스 메소드 호출
p3d1.printNum(p3d1);
}
}
class Point {
protected int x;
protected int y;
static int number;
public static void Hi() {
System.out.println("Hi~");
}
}
class Point3D extends Point // Point 클래스를 상속받은 Point3D 클래스 선언
{
int z;
public void printNum(Point3D p3d1) {
System.out.println("x = " + p3d1.x);
System.out.println("y = " + p3d1.y);
System.out.println("z = " + p3d1.z);
System.out.println("(static)number = " + p3d1.number);
}
}

예상외로 static 으로 선언된 부모의 변수, 메소드까지 문제없이 상속되는 것을 확인할 수 있었다.
앞의 내용을 정리하자면 다음과 같다.
· 부모 클래스에서는 기본 생성자를 만드는 것 외에 아무런 작업이 필요없다.
· 상속 관계를 만들기 위해서는 extends 라는 예약어를 사용한다.
· 자식 클래스의 생성자가 호출되면, 자동으로 부모 클래스의 기본 생성자가 실행된다.
· 자식은 부모 클래스의 public, protected 로 선언된 인스턴스 변수 및 클래스 변수와 메소드에 접근 가능하다.