상속에 대해 알아봅니다. 상속과 클래스 계층구조 메소드 오버라이딩 7장. 상속과 다형성 상속에 대해 알아봅니다. 상속과 클래스 계층구조 메소드 오버라이딩
객체마을에서의 더 나은 삶 여러분도 다형성 계획에 참여해 보세요.
1. 네 클래스에 공통적으로 들어있는 것을 찾아낸다. 다시 보는 의자 전쟁 1. 네 클래스에 공통적으로 들어있는 것을 찾아낸다. Square rotate() playSound() Circle rotate() playSound() Triangle rotate() playSound() Amoeba rotate() playSound()
다시 보는 의자 전쟁 2. 모두 도형이고 회전(rotate()) 및 사운드 재생(playSound()) 기능이 있으므로 공통적인 기능을 모두 뽑아서 Shape이라는 클래스를 만든다. Shape rotate() playSound()
다시 보는 의자 전쟁 3. 나머지 도형 클래스 네 개를 상속이라는 관계로 Shape 클래스와 연관시킴 rotate() playSound() 3. 나머지 도형 클래스 네 개를 상속이라는 관계로 Shape 클래스와 연관시킴 상위클래스(superclass) - 추상적 하위클래스(subclass) - 구체적 Square Circle Triangle Amoeba
다시 보는 의자 전쟁 아메바의 메소드는 어떻게 처리할 수 있을까요? 오버라이드 서브클래스(subclass) Shape rotate() playSound() 오버라이드 서브클래스(subclass) Amoeba rotate() { // 아메바 전용 // 회전 코드 } playSound() { // 사운드 재생 코드 Square Circle Triangle Amoeba
상속의 이해 인스턴스 변수 (상태) 상위클래스 (더 추상적임) 메소드 (행동) 오버라이드하는 하위클래스 메소드 (더 구체적임) SuperHero suit tights specialPower useSpecialPower() putOnSuit() 인스턴스 변수 (상태) 상위클래스 (더 추상적임) 메소드 (행동) 오버라이드하는 메소드 하위클래스 (더 구체적임) FriedEggMan PantherMan useSpecialPower() putOnSuit()
상속의 예 의대를 가지 않아도 상속받기만 하면 됩니다. public class Doctor { boolean worksAtHospital; void treatPatient() { // 진료를 합니다. } public class FamilyDoctor extends Doctor { boolean makeHouseCalls; void giveAdvice() { // 집에서 필요한 조언을 합니다. public class Surgeon extends Doctor { // 외과 수술을 합니다. void makeIncision() { // 살을 쨉니다. 의대를 가지 않아도 상속받기만 하면 됩니다.
연필을 깎으며 인스턴스 변수 한 개 메소드 한 개 Surgeon에 들어있는 인스턴스 변수의 개수는? Doctor worksAtHospital treatPatient() 인스턴스 변수 한 개 메소드 한 개 Surgeon에 들어있는 인스턴스 변수의 개수는? FamilyDoctor에 들어있는 인스턴스 변수의 개수는? Doctor에 있는 메소드 개수는? Surgeon에 들어있는 메소드 개수는? Surgeon treatPatient() makeIncision() FamilyDoctor makeHouseCalls giveAdvice() FamilyDoctor에 들어있는 메소드 개수는? FamilyDoctor에서 treatPatient()를 실행할 수 있을까요? FamilyDoctor에서 makeIncision()을 실행할 수 있을까요?
동물 시뮬레이션을 위한 상속 트리 설계 공통적인 속성과 행동이 들어있는 객체를 찾아봅시다.
상속을 활용한 설계 공통적인 상태와 행동을 나타내는 클래스를 설계합니다. Animal picture food hunger boundaries location makeNoise() eat() sleep() roam 공통적인 상태와 행동을 나타내는 클래스를 설계합니다. Lion Wolf Dog Cat Hippo Tiger
메소드 오버라이딩 특정 하위클래스 유형에서만 적용되는 행동이 필요한지 결정합니다. Animal 클래스에서는 각 하위클래스의 eat()과 makeNoise()를 오버라이드해야 한다는 결론을 내릴 수 있습니다. 짖는 소리는 우리의 개성의 척도라고 할 수 있죠. Animal picture food hunger boundaries location makeNoise() eat() sleep() roam 전 엄청나게 많이 먹어대죠.
추상화 개념의 확장 공통적인 행동이 필요한 하위클래스를 두 개 이상 찾아서 추상화의 개념을 더 폭넓게 활용할 수 있을지 찾아봅니다. 여러 클래스를 살펴보면 Wolf와 Dog에 공통적인 행동이 있고 Lion, Tiger, Cat에도 공통적인 행동이 있다는 것을 알 수 있습니다.
추상화 개념의 확장 Animal picture food hunger boundaries location makeNoise() eat() sleep() roam Lion Wolf Dog Cat Hippo Tiger
클래스 계층구조를 완성해봅시다. Animal picture food hunger boundaries location makeNoise() eat() sleep() roam 클래스 계층구조를 완성해봅시다. Feline roam() Canine roam() Hippo Lion Cat Dog Wolf Tiger
어떤 메소드가 호출될까요? 새로운 Wolf 객체를 만듭니다. Wolf w = new Wolf(); Animal makeNoise() eat() sleep() roam() 새로운 Wolf 객체를 만듭니다. Wolf w = new Wolf(); Wolf에 있는 버전을 호출합니다. w.makeNoise(); Canine에 있는 버전을 호출합니다. w.roam(); Wolf에 있는 버전을 호출합니다. w.eat(); Animal에 있는 버전을 호출합니다. w.sleep(); Canine roam() 객체 레퍼런스에 대해 메소드를 호출하면 그 객체 유형의 메소드 중에서 가장 구체적인 버전이 호출됩니다. Wolf makeNoise() eat()
상속 트리 설계 옷 클래스 상위클래스 하위클래스 옷 바지, 셔츠 바지 서츠 바지 셔츠 상속 테이블 상속 클래스 다이어그램
바보 같은 질문은 없습니다. 메소드를 호출한 클래스 유형에서 시작해서 상속 트리를 따라 올라간다고 했는데 JVM에서 매치되는 것을 전혀 찾을 수 없으면 어떻게 되나요? 그런 문제는 일어나지 않습니다. 컴파일러에서 원천적으로 차단하기 때문이죠. 어떤 클래스에서 메소드를 상속한다면 그 메소드가 반드시 있는지 확인하고 넘어갑니다. JVM에서는 항상 올바른 메소드를 호출합니다.
‘A는 B다’ 테스트 삼각형(Triangle)은 도형(Shape)이다. 고양이(Cat)는 고양이과(Feline)이다. 외과의사는 의사이다. 욕조는 화장실이다…? Tub int size; Bubbles b; Bathroom Tub bathtub; Sink theSink; Bubbles int radius; int colorAmt;
‘A는 B다’ 테스트 Animal makeNoise() eat() sleep() roam() 상속 트리를 제대로 설계했다면 어떤 하위클래스에 대해서도 ‘하위클래스는 상위클래스이다’라는 관계가 성립합니다. B라는 클래스가 A라는 클래스를 확장하면 B 클래스는 A 클래스입니다. C라는 클래스가 B라는 클래스를 확장하면 C 클래스는 B 클래스이며, 또한 A 클래스이기도 합니다. Canine roam() Wolf makeNoise() eat()
바보 같은 질문은 없습니다. 상위클래스에서 하위클래스의 메소드를 쓰고 싶을 때는 어떻게 해야 하나요? 상위클래스에서는 하위클래스에 대해 알 필요가 없습니다. 부모가 자식으로부터 상속을 받는 일이 없는 것처럼 상위클래스에서 하위클래스의 메소드를 만들 수는 없습니다.
바보 같은 질문은 없습니다. 하위클래스에서 상위클래스에 있는 버전의 메소드와 새로 오버라이드한 버전의 메소드를 모두 사용하고 싶다면 어떻게 해야 할까요? super라는 키워드를 사용하면 됩니다. public void roam() { super.roam(); // 새로 추가할 내용 }
네 가지 접근 단계 private default protected public public 상속됩니다. private int AccountNum; public void roam() { }
상속 트리 설계시 주의할 점 어떤 클래스가 다른 클래스(상위클래스)를 더 구체화한 유형이라면 상속을 활용합니다. 버드나무 – 나무 자동차 – 승용차 같은 일반적인 유형에 속하는 여러 클래스에서 공유해야 하는 어떤 행동이 있다면 상속을 활용합니다. Square, Circle, Triangle – Shape
핵심정리 하위클래스는 상위클래스를 확장합니다. 하위클래스는 상위클래스에 있는 모든 public으로 지정한 인스턴스 변수와 메소드를 상속합니다. private으로 지정한 변수와 메소드는 상속하지 않습니다. 메소드는 오버라이드할 수 있지만 인스턴스 변수는 오버라이드할 수 없습니다. ‘A는 B다’ 테스트를 활용하여 상속 계층이 올바른지 확인합시다. ‘A는 B다’ 관계는 한 방향으로만 작동합니다. 하위클래스에서 메소드를 오버라이드하면, 그리고 하위클래스의 인스턴스에 대해 그 메소드를 호출하면 오버라이드된 버전의 메소드가 호출됩니다. B라는 클래스가 A라는 클래스를 확장하고 C는 B를 확장한다면 B는 A이고 C는 B이면서 또한 A가 됩니다.
상속을 활용할 때의 장점 코드가 중복되는 것을 방지할 수 있습니다. 공통적인 코드를 한 군데 모아놓고 하위클래스에서 상위클래스로부터 상속을 받을 때 그 코드도 받게 합니다. 행동을 변경하고 싶으면 한 군데만 변경하면 나머지 모든 하위클래스에서 변경된 기능을 활용할 수 있습니다. 일련의 클래스를 위한 공통적인 규약(protocol)을 정의합니다.
모든 Animal 객체에서 여기 있는 것과 같은 네 가지 일을 할 수 있다는 것을 공표합니다. 상속을 통한 규약 정의 상위클래스에서 메소드를 정의하면 그 메소드는 하위클래스로 상속될 수 있으며 그 메소드 정의는 일종의 규약이라고 할 수 있습니다. 모든 Animal 객체에서 여기 있는 것과 같은 네 가지 일을 할 수 있다는 것을 공표합니다. Animal makeNoise() eat() sleep() roam()
객체 선언 및 대입 과정 1. 레퍼런스 변수 선언 Dog myDog = new Dog();
객체 선언 및 대입 과정 2. 객체 생성 Dog myDog = new Dog(); Dog 객체
객체 선언 및 대입 과정 3. 객체와 레퍼런스 연결 Dog myDog = new Dog(); Dog 객체
객체 선언 및 대입 과정 레퍼런스 유형과 객체 유형이 똑같아야 합니다. Dog 객체 myDog Dog
Animal myDog = new Dog(); 다형성 활용 Animal myDog = new Dog(); Dog 객체 myDog Animal
다형성 활용 다형성을 사용하면 레퍼런스 유형을 실제 객체 유형의 상위클래스 유형으로 지정할 수 있습니다. Animal animals = new Animal[5]; animals[0] = new Dog(); animals[1] = new Cat(); animals[2] = new Wolf(); animals[3] = new Hippo(); animals[4] = new Lion(); for (int i = 0; i < animals.length; i++) { animals[i].eat(); animals[i].roam(); }
다형적인 인자, 리턴 유형 인자와 리턴 유형에 대해서도 다형성을 적용할 수 있습니다. class Vet { public void giveShot(Animal a) { // ‘a’ 매개변수가 가리키는 Animal 객체에 // 주사를 놓습니다. a.makeNoise(); } a
다형적인 인자, 리턴 유형 하위클래스를 새로 만들더라도 코드를 바꿀 필요가 없습니다. class PetOwner { public void start() { Vet v = new Vet(); Dog d = new Dog(); Hippo h = new Hippo(); v.giveShot(d); v.giveShot(h); } 하위클래스를 새로 만들더라도 코드를 바꿀 필요가 없습니다.
바보 같은 질문은 없습니다. 하위클래스의 단계에 실질적인 제한이 있나요? 얼마나 깊이 들어갈 수 있어요? 그런 제한이 따로 정해져 있지는 않습니다. 프로그래밍을 하다 보면 상속트리를 얕게 만드는 게 좋다는 것을 자연스럽게 깨달을 수 있을 것입니다.
바보 같은 질문은 없습니다. 소스 코드를 직접 고칠 수 없는데 메소드 작동 방식을 바꾸고 싶을 때 하위클래스를 만들어서 그렇게 할 수 있나요? 물론 가능합니다. 그리고 OO의 장점 가운데 하나라고 할 수 있습니다. 클래스를 완전히 새로 만들거나 누가 그 클래스를 처음 만들었는지 찾아내는 것보다는 훨씬 쉬운 방법이죠.
바보 같은 질문은 없습니다. 아무 클래스나 확장할 수 있나요? 아니면 클래스 멤버와 마찬가지로 클래스를 private로 지정하면 상속할 수 없다던가 하는 제한이 있나요? 내부 클래스를 제외하면 private으로 지정하거나 하는 식으로 상속을 할 수 없는 클래스를 만드는 방법은 없습니다. 하지만 하위클래스를 만들 수 없도록 하는 방법은 있습니다. 클래스를 public으로 선언하지 않는 방법 final 변경자를 쓰는 방법 클래스 생성자를 모두 private으로 지정하는 방법
바보 같은 질문은 없습니다. 굳이 하위클래스를 만들지 못하게 하는 이유가 있나요? 보안상의 이유 때문에 종종 그렇게 하기도 합니다. 여러분이 직접 만든 클래스에 대해 final 변경자를 쓰는 일은 없겠지만 String 같은 클래스의 경우에는 보안상의 문제 때문에 final로 지정되어있습니다.
바보 같은 질문은 없습니다. 하위클래스는 만들 수 있도록 하면서 메소드만 오버라이드할 수 없도록 하는 방법도 있나요? 메소드에만 final 변경자를 사용하면 됩니다. 클래스에 있는 모든 메소드를 오버라이드할 수 없도록 하고 싶다면 클래스 자체를 final로 지정해도 됩니다.
오버라이드 규칙 오버라이드하는 메소드의 인자와 리턴 유형은 외부에서 보기에 상위클래스에 있는 메소드와 완벽하게 일치해야 합니다. Appliance boolean turnOn() boolean turnOff() Appliance public boolean turnOn() public boolean turnOff() Toaster boolean turnOn(int level) Toaster private boolean turnOn()
메소드 오버로딩 오버로딩 (Overloading) 이름이 같고 인자 목록은 다른 메소드를 두 개 이상 만드는 것 같은 메소드를 여러 다른 버전으로 만들 수 있습니다. 리턴 유형이 달라도 됩니다. 리턴 유형만 바꿀 수는 없습니다. 접근 단계를 바꿔도 됩니다.
오버로딩의 예 public class Overloads { String uniqueID; public int addNums(int a, int b) { return a + b; } public double addNums(double a, double b) { public void setUniqueID(String theID) { // 여러 검증 과정 후에 다음을 실행 uniqueID = theID; public void setUniqueID(int ssNumber) { String numString = “” + ssNumber; setUniqueID(numString);
숙제 본문을 다시 한 번 꼼꼼히 읽어봅시다. 본문 및 맨 뒤에 나와있는 연습문제를 직접 풀어봅시다.