티스토리 뷰

* 본 게시물은 HeadFirst Java 책을 공부목적으로 정리한 것 입니다.

7. 상속과 다형성

의자 전쟁

1. 클래스 네 개에 공통적으로 들어있는 것 찾아냄.

2. 네 개는 전부 도형(Shape)에 속하고, 모두 그 도형을 회전(rotate)시키고 사운드를 재생(playSound)하는 기능을 함. 그래서 공통적인 기능을 뽑아서 Shape라는 새로운 클래스에 집어넣음.

3. 이때 Shape클래스는 다른 네 클래스의 상위클래스, 나머지 클래스 네 개는 Shape의 하위 클래스가 됨. 하위클래스는 상위클래스의 메소드를 상속 함. Shape클래스에 어떤 기능이 있으면 하위클래스에서도 자동적으로 같은 기능 발휘.

4. Ameba 클래스에서 상위클래스인 Shape의 rotate()와 playSound() 메소드를 오버라이드함.

오바라이딩은 “하위클래스에서 메소드의 역할을 변경하거나 확장할 필요가 있을 때 상속받은 메소드를 새로 정의하는 것”을 의미한다.

상속의 이해

상속을 이용하여 설계할 때는 공통적인 코드를 어떤 클래스에 넣은 다음, 다른 더 구체적인 클래스에 공통적인(더 추상적인)클래스가 상위클래스라는 것을 알려주면 됩니다. 한 클래스가 다른 클래스를 상속하는 것을 하위클래스가 상위클래스로부터 상속받는다고 말함.


자바에서는 하위클래스가 상위클래스를 확장(extend)한다고 함. 상속이라는 관계는 하위클래스가 상위클래스의 멤버를 물려받는다는 것을 의미. 인스턴스 변수와 메소드를 “클래스의 멤버”라고 부름.


인스턴스 변수는 오버라이드 할 필요가 없기 때문에 오버라이드하지 않음. 인스턴스 변수에서 특별한 행동을 정의하는 것이 아니기 때문에 하위클래스에서는 상속받은 인스턴스 변수를 그대로 사용하고 그 값을 마음대로 선택.


객체 레퍼런스에 있는 메소드를 호출하면 그 객체 형식의 메소드 중에서 가장 구체적인 버전이 호출됨. 즉, 가장 아래 있는 것 호출 됨.

어떤 클래스에서 메소드를 상속한다면, 그 메소드가 반드시 들어있음.


상위클래스 > 더 추상적 ex) 옷

하위클래스 > 더 구체적 ex) 바지, 셔츠

상속관계 > ‘A는 B다.’ = A는 B를 확장한다.


하위클래스는 상위클래스의 멤버를 상속받는다.

일단 멤버에는 인스턴스 변수와 메소드가 있다. 그리고 상위클래스에서는 멤버에 대해 접근 단계를 지정하여 하위클래스에서 상속받을 수 있는 것과 없는 것을 지정할 수 있다.

private default protected public

접근단계를 통해 누가 그 멤버를 볼 수 있는지 제어 가능.

Public으로 지정한 멤버는 상속 O

Private으로 지정한 멤버는 상속 X


상속을 활용하여 설계할 때 주의점

어떤 클래스가 다른 클래스(상위클래스)를 더 구체화한 형식이라면 상속을 활용.

ex) 버드나무는 나무를 구체화한 것 > 확장 ok

같은 일반적인 형식에 속하는 여러 클래스에서 공유해야 하는 어떤 행동이 있다면 상속을 활용. ex)Square, Circle, Triangle에는 모두 그 도형을 회전시키고 소리를 재생하는 메소드가 필요. 따라서 그런 기능을 Shape라는 상위클래스에 집어넣는 것이 자연스럽고, 그렇게 하면 클래스의 관리와 확장이 용이.

그러나 객체지향 프로그래밍에 있어서 상속이 핵심 기능 가운데 하나긴 하지만 행동을 재사용하는 데 있어서 무조건 최선의 방법이 아니라는 점 주의. 디자인 패턴을 공부하다 보면 다른 강력하면서도 유연한 방법 익힐 수 있을 것.


상위클래스와 하위클래스 사이의 관계가 위에 있는 두 가지 규칙에 위배된다면, 어떤 코드를 다른 클래스에서 재사용할 수 있다는 이유만으로 상속을 사용하면 안됨.

ex)Alarm이라는 클래스에서 특별한 출력 코드를 만들었는데, Piano라는 클래스에서도 출력하는 코드가 필요해서 Alarm에 있는 코드를 재사용하기 위한 용도로 확장하면 안됨.

Piano는 Alarm을 구체화 한 것이 아니기 때문.(이런 경우에는 출력 코드를 Printer같은 클래스에 집어넣고 출력을 하는 모든 객체에서 이런 클래스를 활용하여 출력하는 것이 좋음.


하위클래스와 상위클래스 사이에서 ‘A는 B다’ 관계가 성립하지 않는다면 상속을 사용하면 안됨. 항상 하위클래스가 상위클래스를 구체화한 형식인지 생각해 볼 것.


상속의 장점

1. 코드가 중복되는 것을 방지할 수 있음.

> 공통적인 코드를 한 군데 모아놓고 하위클래스에서 상위클래스로부터 상속을 받을 때 그 코드도 받게 함. 그 행동을 변경하고 싶으면 한 군데만 변경하면 나머지 모든 하위클래스에서 변경된 기능 활용가능.

2. 일련의 클래스를 위한 공통적인 규약(protocol)를 정의.

> 상속을 사용하면 특정 상위클래스 밑에 모여있는 모든 클래스에, 상위클래스에 들어있는 모든 메소드가 들어가게 할 수 있음. 즉, 상속을 통해 연관된 일련의 클래스에 대해 공통적인 규약을 정의할 수 있음.

다형성을 배워봅시다

Dog myDog = new Dog();


1. 레퍼런스 변수를 선언

> JVM에 레퍼런스 변수를 위한 공간을 할당하도록 지시. 레퍼런스 변수의 유형은 한 번 정해지면 바뀌지 않음.

2. 객체를 만듬  

> JVM에 가비지 컬렉션 기능이 있는 힙에 새로운 Dog객체를 위한 공간을 할당하도록 지시

3. 객체와 레퍼런스를 연결

> 리모컨과 제어할 객체 연결

*중요한 것은 레퍼런스 유형과 객체 유형이 똑같아야 함.

위의 예제는 같은 유형이었음.


하지만, 다형성을 활용하면 레퍼런스와 객체가 다른 유형이어도 됨.

Animal myDog = new Dog();

다형성을 사용하면 레퍼런스 유형을 실제 객체 유형의 상위클래스 유형으로 지정 할 수 있음.

이렇게 하면 다형적인 배열을 만드는 것과 같은 일을 할 수 있습니다.


//Animal 유형의 배열, 즉 Animal유형의 객체를 저장할 수 있는 배열을 선언 함.
Animal[] animals = new Animal[5];

//Animal 배열에는 Animal의 하위클래스에 속하는 모든 객체를 집어넣을 수 있음.
animals [0] = new Dog();
animals [1] = new Cat();
animals [2] = new Wolf();
animals [3] = new Hippo();
animals [4] = new Lion();

//이 부분이 다형성과 관련하여 가장 중요한 부분. 배열의 모든 원소에 대해 순환문을 돌리면서 Animal 클래스에 들어있는 메소드 중 하나를 실행시키면, 각 객체마다 올바른 메소드를 실행하는 것을 확인 할 수 있음.
for(int i = 0; i <animals.length; i++){
//i가 0이면 배열의 0번 인덱스 위치에는 Dog객체가 들어있으므로 Dog의 eat()메소드를 호출
   animals[i].eat();
   animals[i].roam();

}

메소드 오버라이딩

상위클래스의 메소드를 오버라이드할 때는 계약을 이행해야 합니다.

ex) “인자를 받지 않을 것이며 부울 값을 리턴하겠음”

즉, 오버라이드하는 메소드의 인자와 리턴 형식은 외부에서 보기에 상위클래스에 있는 오버라이드 당하는 메소드와 완벽하게 일치해야 함.

1. 인자는 똑같아야 하고, 리턴 유형은똑같은 유형 or 하위클래스 유형 리턴.

2. 메소드를 더 접근하기 어렵게 만들면 안됨.

즉, 내용만 다르고 나머지(인자,리턴유형)는 같아야 함.

메소드 오버로딩

메소드 오버로딩은 이름이 같고 인자목록이 다른 메소드 두 개를 만드는 것.

오버로드된 메소드는 이름만 같을 뿐 그냥 서로 다른 메소드라서 상속이나 다형성하고는 전혀 상관이 없음. 오버로딩과 오버라이딩은 서로 다른 개념.

오버로딩을 활용하면 호출하는 쪽의 편의를 위해 같은 메소드를 서로 다른 인자 목록을 가진 여러 버전으로 만들 수 있음. 오버로드 하는 메소드에서는 상위클래스에서 정의한 다형성 계약을 이행하지 않아도 되기 때문에 메소드 오버로딩은 훨씬 더 융통성이 좋다.

1. 리턴 유형이 달라도 됨.

> 메소드를 오버로드 할 때는 인자 목록만 다르면 리턴 유형을 마음대로 바꿔도 됨.

2. 리턴 유형만 바꿀 수는 없음.

> 메소드를 오버로딩할때는 리턴 유형하고는 무관하게 인자 목록을 반드시 변경해야함.

3. 접근 단계를 마음대로 바꿀 수 있음.

> 메소드를 오버로드해서 더 제한이 심한 메소드를 만들어도 됨.

새로운 메소드가 오버로드된 메소드의 계약 조건을 이행해야 하는 것은 아니기 때문에.

즉, 이름만 같고 인자는 무조건 달라야 함. 아예 다른 메소드.



댓글