끝나지 않는 프로그래밍 일기


1. 상속


이번 강좌에서는 접근 제한자를 잠시 뒤로 미루고, 상속에 대해서 알아보도록 하겠습니다. 죄송합니다. 


여기서, 상속(Inheritance)이란 말 그대로 '부모의 유산을 물려받다'를 의미하고 이는 '자식이 부모의 것을 가진다'라고 할 수 있습니다. 객체 지향 프로그래밍에서도 이와 비슷한 개념으로 쓰이는데, 여기에서는 부모 클래스에 정의된 멤버를 자식 클래스가 물려받는 것을 말합니다. 즉, 상속을 통해 기존에 있던 클래스(부모 클래스)를 이용하여 새로운 클래스를 만들 수 있습니다. 이는, 기존의 것을 이용하여 만들어내기 때문에 적은 양의 코드로 새로운 클래스를 만들어 낼 수 있습니다. 


상속을 해주는 부모 클래스는 상위 클래스(슈퍼 클래스, Super Class) 또는 기반 클래스(베이스 클래스, Base Class)라 하며, 상속을 받는 자식 클래스를 하위 클래스(서브 클래스, Sub Class), 또는 파생 클래스(유도 클래스, derived class)라고 합니다. 만약, 자바에서 상속을 받게 해주려면, 새로운 클래스 이름 뒤에 extends와 상속받고자 하는 클래스를 입력해주시면 됩니다.


...

class A { // A라는 클래스 생성

int num;

}


class B extends A { // B 클래스가 생성되고 그와 동시에 A 클래스를 상속받는다.

int num1;

}


class C extends B { // C 클래스가 생성되고 그와 동시에 B 클래스를 상속받고 B 클래스에 상속된 A 클래스도 함께 상속받는다.

int num2;

}

...


예제를 살펴보면, 클래스가 생성되고 num이라는 멤버변수를 A 클래스에 추가했습니다. 그 후에 B 클래스를 생성하고 A 클래스를 상속받죠. 즉, B 클래스에서 A 클래스의 모든 멤버를 가집니다. A 클래스의 num 멤버변수가 추가된 것이나 마찬가지죠. 그리고 마지막으로 C 클래스를 생성하면서 B 클래스를 상속받습니다. 그런데 여기서, B 클래스가 A 클래스에 상속됬기 때문에 C 클래스는 B 클래스와 A 클래스를 모두 상속받습니다.


여기서 상속의 특징을 잠깐만 살펴보겠습니다.


다중 상속이 불가능합니다. 즉, 2개 이상의 클래스를 한꺼번에 상속할 수 없습니다.

 부모의 생성자는 상속이 되지 않습니다.

부모 클래스가 가진 멤버변수와 메소드를 모두 상속받습니다.

부모 클래스 내에서 멤버 변수 또는 메소드가 private 접근 제한자를 사용하면 멤버 변수는 상속 받으나 바로 접근이 불가능하며, 메소드는 상속 되지 않는다.

static 메서드 또는 변수도 상속이 된다.

동일한 이름의 변수가 부모 클래스와 자식 클래스에 둘 다 존재할 경우 부모 클래스의 변수는 가려진다.


확실한 이해를 위해 아래의 예제를 살펴봅시다.


class ParentClass {

public void parentMethod()

{

System.out.println("부모 클래스의 parentMethod가 호출되었습니다.");

}

}


class ChildClass extends ParentClass{

public void childMethod()

{

System.out.println("자식 클래스의 childMethod가 호출됬습니다.");

System.out.println("부모 메소드의 parentMethod를 호출했습니다.");

parentMethod();

}

}


class InheritanceExample {

public static void main(String[] args)

{

ChildClass child=new ChildClass();


System.out.println("간단한 자바 상속 예제: ");

System.out.println("자식 클래스의 childMethod를 호출했습니다.");

child.childMethod();

}

}


결과:

간단한 자바 상속 예제:

자식 클래스의 childMethod를 호출했습니다.

자식 클래스의 childMethod가 호출됬습니다.

부모 메소드의 parentMethod를 호출했습니다.

부모 클래스의 parentMethod가 호출되었습니다.


위의 코드에서 InheritanceExample 클래스 내에있는 메인 메소드 내용을 봅시다. ChildClass 객체를 생성한 뒤에, 자식 클래스의 childMethod를 호출합니다. 그리고 자식 클래스 내에서 부모 클래스 내의 parentMethod를 호출합니다. 결과를 보시면 '자식 클래스의 childMethod 호출 -> childMethod 메소드 내에서 부모 클래스의 parentMethod 호출'이 됨을 확인하실 수 있습니다.


2. 상속에서의 생성자


위에서 동일한 이름의 변수가 부모 클래스와 자식 클래스에 둘 다 존재할 경우 부모 클래스의 변수는 가려진다고 했습니다. 그렇다면 어떻게 부모 클래스의 변수를 이용할 수 있을까요? 우리가 이제 배우게 될 super라는 예약어로 해결이 가능합니다. 이 예약어를 사용하면 상위 클래스의 생성자에도 접근을 할 수 있게되죠.


바로 아래의 예제를 봅시다.


class ParentClass {

private int x;

private int y;


public ParentClass(int x, int y) 

{

this.x = x;

this.y = y;

}

public void xyDisplay()

{

System.out.println("ParentClass, x = " + x + ", y = " + y);

}

}


class ChildClass extends ParentClass{

private int x;

private int y;


public ChildClass(int x, int y)

{

super(x * 3, y * 2);

this.x = x;

this.y = y;

}

public void childMethod()

{

super.xyDisplay();

xyDisplay();

}

public void xyDisplay()

{

System.out.println("ChildClass, x = " + x + ", y = " + y);

}

}


class InheritanceExample {

public static void main(String[] args)

{

ChildClass child=new ChildClass(44, 7);


child.childMethod();

}

}


우선 InheritanceExample 클래스 내에 있는 메인 메소드 내용부터 봅시다. ChildClass 객체를 생성하면서 ChildClass의 생성자를 호출하며 44와 7을 매개변수로 넘깁니다. 그러자 ChildClass 생성자 내에서 super라는 예약어를 만나 부모 클래스의 생성자를 호출하며 x를 3배한 값과 y를 2배한 값을 넘깁니다. 즉 x는 44, y는 7이므로 x의 3배인 132와 y의 2배인 14가 넘어가서 부모 클래스의 멤버변수인 x, y는 각각 132와 14로 초기화됩니다.


그 후에 자식 클래스의 x에다 매개변수 x의 값을 넣습니다. 매개변수 x의 값은 44, y의 값은 7이므로 자식 클래스의 멤버 변수에 들어가는 값은 각각 44, 7로 초기화됩니다. 여기서 this 키워드는 자기 자신의 멤버를 가르킬때 사용됩니다. 즉 this.x와 this.y는 ChildClass의 멤버변수 x, y를 의미하죠. 그리고 빠져나와서 InheritanceExample 클래스로 돌아가 ChildClass 객체 내의 childMethod를 호출합니다. 


childMethod에는 super 예약어를 사용해서 부모 클래스의 xyDisplay 메소드를 호출합니다. super 예약어를 이용하는 이유는 자식 클래스의 xyDisplay 메소드에 의해 부모 클래스의 xyDisplay가 가려지기 때문입니다. 부모 클래스의 xyDisplay에서, 부모 클래스의 멤버변수인 x, y의 값을 출력하고 빠져 나온뒤에 자식 클래스의 xyDisplay를 호출합니다. 여기서도 자식 클래스의 멤버변수 x, y의 값을 출력하고 빠져나온 뒤에 끝이나죠.


자, 여기서 super 예약어를 어떻게 사용하는지 정리해봅시다.


부모 클래스의 멤버 변수 접근: super.멤버변수

부모 클래스의 멤버 메소드 접근: super.멤버메소드(매개변수);

부모 클래스의 생성자 호출: super(매개변수);


그리고 super 예약어를 사용하면서 주의하셔야 할 점들이 몇가지 있습니다.


반드시 자식 클래스의 생성자 첫 라인에서 부모의 생성자를 호출해야 함.

자식 클래스의 생성자 내에서 반드시 부모의 생성자를 호출해야 함.

명시적으로 자식의 클래스에서 부모의 생성자를 호출하지 않아도 super가 자동 삽입되어 부모 클래스의 생성자를 호출 함.


위에서, 자식 클래스의 생성자 내에서 반드시 부모의 생성자를 호출해야 한다고 말했는데 우리는 여태까지 부모의 생성자를 호출하지 않아도 에러가 나지 않았습니다. 그 이유는, A라는 부모 클래스와 B라는 자식 클래스가 존재한다고 가정하고, A, B 클래스에서 명시적으로 생성자를 정의하지 않았을 경우에는 가상 머신에 의해서 디폴트 생성자가 

 알아서 추가됩니다. A는 내용이 텅 빈 디폴트 생성자가 추가되나, 자식 클래스인 B의 경우에는 super();가 자동 삽입됩니다. 그렇기에 에러가 나지 않았던 겁니다.


3. 메소드 오버로딩, 오버라이딩


메소드 오버로딩(Method Overloading)이란, 부모 클래스와 자식 클래스가 서로 상속관계에 있고, 그 두개의 클래스에서 동일한 메소드가 존재하며 인자의 수와 데이터 타입이 다를 경우를 말합니다. 아래의 예제를 봅시다.


class ParentClass {

public void DisplayMethod(String str)

{

System.out.println("ParentClass의 DisplayMethod(String str) 호출: " + str);

}

}


class ChildClass extends ParentClass{

public void DisplayMethod()

{

System.out.println("ChildClass의 DisplayMethod() 호출");

}

}


class InheritanceExample {

public static void main(String[] args)

{

ChildClass child=new ChildClass();


child.DisplayMethod();

child.DisplayMethod("JAVA!");

}

}


결과:

ChildClass의 DisplayMethod() 호출

ParentClass의 DisplayMethod(String str) 호출: JAVA!


위의 코드에서 InheritanceExample 클래스 내의 메인 메소드 안을 보면, 객체가 생성되고 그 객체의 DisplayMethod를 호출합니다. 그런데 하나는 인자가 없는 DisplayMethod를 호출하고 있고, 또 하나는 인자가 있는 DisplayMethod를 호출하고 있습니다. 인자가 없을 경우엔 자식 클래스의 DisplayMethod가 호출되고, 인자가 있을 경우에는 부모 클래스의 DisplayMethod가 호출됩니다.


반대로 오버라이딩(Overriding)은 뭘까요? 오버라이딩은 메소드의 이름이 서로 같고, 인자가 같고, 반환형이 같을 경우에 상속받은 메소드를 덮어쓰는 것을 말합니다. '부모 클래스의 기존 메소드는 무시해버리고 자식 클래스의 메소드의 기능을 사용하겠다'란 말과 같습니다. 예제를 봅시다.


class ParentClass {

public void DisplayMethod()

{

System.out.println("ParentClass의 DisplayMethod() 호출");

}

}


class ChildClass extends ParentClass{

public void DisplayMethod()

{

System.out.println("ChildClass의 DisplayMethod() 호출");

}

}


class InheritanceExample {

public static void main(String[] args)

{

ChildClass child=new ChildClass();


child.DisplayMethod();

}

}


결과:

ChildClass의 DisplayMethod() 호출


이번에도 역시 객체가 생성되고 그 객체의 DisplayMethod를 호출하는 예제입니다. 그런데 부모 클래스와 자식 클래스 사이에 동일한 메소드가 존재하고 있고 반환형도, 인자도 똑같습니다. 여기서 오버라이딩이 이루어지며 부모 클래스의 메소드를 무시하고 자식 클래스의 메소드를 호출합니다.


오늘 강좌는 여기서 마무리 짓도록 하겠습니다. 수고하셨습니다. 다음 강좌에서는 접근 제한자에 대해 설명합니다.