SUPPORT

상속, 다형성, 오버라이딩, 추상클래스, 추상메소드

No Comment 2019년 1월 2일 40 (0)
개발 스터디상속, 다형성, 오버라이딩, 추상클래스, 추상메소드

상속 :

▣ 상속 트리를 잘 설계했다면 ‘하위클래스 = 상위클래스’ 의 관계가 성립한다.

ex) 스포츠카 = 자동차. (o) , 화장실 = 욕조 (x)

원숭이는 포유류다 (o) => ‘자식클래스’는 ‘부모클래스’다. 성립.

포유류는 원숭이다 (x) => ‘부모클래스’는 ‘자식클래스’다. 는 성립 X .

이 테스트를 활용하면 상속 계층 설계를 올바로 했는지를 단순 테스트 할 수 있다.

 

상속 클래스의 특징 1.

클래스는 상위/하위 클래스 관계로 계층화 될 수 있다.

Shape 클래스에 정의되어 있는 rotate(), playSound() 메소드는 Shape 클래스를 상속받는 하위클래스가 모두 공통적으로 가지는 특징들이다. (동물이 하는 행위 중 잔다는 행위를 sleep() 메소드로 정의한다면, 동물 클래스를 상속받은 강아지 클래스, 고양이 클래스 등등 모두 sleep() 메소드를 가질 수 있는 것이다.)

 

클래스의 특징 2: 하위 클래스는 상위 클래스의 속성들을 모두 ‘상속’받는다.

사각형, 삼각형, 원 모양의 버튼이 돌아가고 사운드를 재생하는 동작은 모두 같은 원리로 동작을 했는데, 뒤늦게 추가된 아메바형 버튼은 기존의 것들과 메커니즘이 완전히 달라졌다. 돌리는 방식도 달라야 하고, 재생해야 하는 사운드의 형식도 달라져야 한다. 이 때 우리가 사용하는 것이 오버라이딩(Overriding)이라고 하는 개념이다. 오버라이딩이란 하위 클래스가 상위 클래스의 속성(여기서는 메소드)들을 상속받을 때, 본인에게 맞는 형식으로 재구성할 수 있다는 의미이다. 동물이 모두 잠은 자지만 종마다 자는 방식은 조금씩 다르다. 기린은 서서 잔다고 하고, 바다 동물들은 떠다니면서 자겠지? 등등. 그 중에서도 가장 보편적인 잠자는 방식, ‘해가 떨어지면 편안한 곳에 누워서 눈을 감고 해가 뜰 때까지 잔다’고 동물 클래스의 sleep() 메소드에서 정의를 했다면, 이 방식을 따르지 않는 하위 클래스들은 본인만의 잠자는 방식을 다시 정의해야 한다. 기린 클래스의 sleep() 메소드는 ‘낮에 주변에 맹수가 없을 때 서서 눈 감고 쪽잠을 잔다’는 식으로 말이다

 

다형성

다형성이란 상위클래스로 선언된 레퍼런스를 이용하여 하위클래스  객체를 참조하는 것이다.

 

코드 첫번째 줄은 우리가 지금까지 이해하고 있던 방식의 Dog 객체 생성과 대입 방법이다. 이 한 줄의 코드는 사실 3단계를 거쳐 완성되는데,

Dog myDog : 레퍼런스 변수 myDog를 선언한다.

new Dog() : Dog 클래스의 객체를 할당한다.

= : Dog 클래스의 객체 레퍼런스를 myDog에 대입한다.

코드 두번째 줄은 다형성을 활용한 객체 생성으로, Animal 레퍼런스 변수에 Dog 객체를 대입하는 것인데, Dog가 Animal 클래스를 상속하였기 때문에 가능한 연산이다.

Animal myDog = new Dog(); 에서 myDog 레퍼런스는 Dog 객체가 Animal 인 척 하고 있는 상황인데, 어차피 Dog는 one of Animal 이기 때문에 가능한 것이다. 반대의 경우는, Animal이라고 모두 Dog는 아니기 때문에 성립하지 않는다.

 

추상 클래스

o 추상 클래스의 필요성

우리는 상속의 개념과 상속을 효율적으로 활용하기 위한 다형성의 개념을 살펴보았다.  Animal이라는 상위 클래스를 상속하는 여러 종류의 동물 클래스(Dog, Cat, Wolf 등)을 만들어 놓고, 다형성을 활용하면 여러 동물들을 모두 Animal이라는 상위 클래스로 취급할 수 있어서 프로그램을 더 똑똑하게 구성할 수 있다는 것은 이해했을 것이다.

그런데, 여기서 한 가지 의문점이 생긴다. 지금까지 우리가 이해한 바로는 모든 클래스는 그저 하나의 틀일 뿐이고, 실제로 클래스의 내용을 사용하려면 Animal a = new Animal(); 처럼 인스턴스를 생성해야하는데, Animal의 인스턴스는 과연 어떻게 생긴 애일까?

Dog, Cat, Wolf 등은 실제 하는 동물을 나타내는 클래스이기 때문에 인스턴스를 만들면 강아지 한 마리, 고양이 한 마리 처럼 실체가 있는 데 반해, Animal 클래스는 ‘동물’이라는 추상적인 개념을 표현한 것이지 실체가 없다. 인스턴스가 하나의 존재(객체)를 나타낸다고 하면, Animal 클래스의 인스턴스를 만든다는 것은 실제하지 않는 것의 실체를 만든다는 의미가 된다.

이 모호한 상황을 해결하기 위해서 필요한 개념이 바로 “추상 클래스”이다.

 

추상 클래스란, 인스턴스를 만들 수 없는 클래스를 의미한다. 실체가 없는 빈 껍데기이기 때문에 실존 객체를 만들 수 없다는 것이다.

하지만, 여기서 주의해야 하는 점은 추상 클래스는 “인스턴스”를 만들 수 없는 것이지, 레퍼런스 변수를 만들 수 없다는 것을 의미하지는 않는다. 다시 한번 인스턴스 생성 과정을 되짚어 보면,

1. Animal a : Animal 객체의 레퍼런스를 저장할 수 있는 변수를 하나 생성한다.

2. new Animal() : Animal 클래스의 인스턴스를 생성한다.

3. =: 2에서 생성한 인스턴스의 레퍼런스를 1의 레퍼런스 변수에 저장한다.

 

추상 클래스는 “인스턴스”를 생성할 수 없기 때문에, 2번 new Animal() 과정을 할 수 없는 것이지, 1번 레퍼런스 변수는 생성할 수 있다.

Animal a = new Dog();

위에서처럼 우리가 다형성을 사용할 수 있었던 것도, Animal 레퍼런스를 저장하는 변수에 Dog나 Cat 의 레퍼런스를 담았기 때문이었음을 생각해보자.

추상클래스는 인스턴스를 만들 수 없는데 어떻게 쓰라는 거지? 의문이 들 수 있다. 추상 클래스는 다형성을 활용해서 실질적으로 동작할 수 있고, 그 말은 하위클래스의 인스턴스를 추상 클래스 레퍼런스에 대입하여 사용해야 한다는 것을 의미하므로, 결국 추상클래스를 사용하기 위해서는 반드시 이를 상속받는 클래스 중 인스턴스를  생성할 수 있는(== abstract class가 아닌 == 실체가 있는 클래스(Dog, Cat과 같은 / 책에서는 이를 구상클래스라고 한다.))를 통해야 한다.(* 주의: 추상클래스를 상속받아 추상클래스도 만들 수도 있다. 하지만 이 경우에도 하위클래스를 따라가다가 보면 결국은 적어도 1개의 구상클래스가 존재해야 한다.

 

클래스 뿐만 아니라 클래스 내부의 메소드도 abstract로 지정하여 추상화할 수 있다. 추상 메소드는 이름만 있고 실제 어떤 동작을 하는지는 빈칸으로 내버려둔 메소드를 의미한다. 그렇기 때문에 해당 추상 메소드를 사용하기 위해서는 반드시 하위클래스에서 오버라이드해야한다. (오버라이드란, 하위클래스에서 상위클래스의 특정 메소드를 재정의하는 것을 의미한다.)

/* 추상 메소드도 abstract key-word로 나타내며,

아래처럼 메소드의 operation 부분을 공백으로 둔다. */

public abstract void eat();

 

추상 메소드를 가진 클래스의 인스턴스를 생성할 수 있을까? 당연히 없을 것이다. 인스턴스를 생성한다는 것은 하나의 생명체가 태어난 것과 비교할 수 있는데, 추상 메소드를 가진 클래스의 객체를 만든다는 것은, 존재를 만들어 놓고, eat()을 어떻게 하는 건지 전혀 안 알려주고 무작정 하라고 시키는 것과 똑같은 것이다. 컴퓨터는 알다시피 개발자가 시키는대로 하는 수동적인 존재인데, eat()이라는 메소드가 뭐하는 애인지 전혀 알지 못하는데, 이를 실행하라고 시키면 컴퓨터가 당황하기 때문에 메소드에 정확한 명령어를 알려주는게 중요하다.

 

Leave a Reply

댓글작성시 Code-Highlighter 삽입방법