안녕하세요 Limky 입니다.
이번 시간은 객체지향에서 정말 중요한 "다형성"에 대해서 알아보겠습니다.
다형성은 같은 타입이지만 실행 결과가 다른 것을 뜻합니다.
즉 동일한 타입에 다양한 객체를 이용하여 다양한 결과를 만들 수 있습니다.
이런 다양성을 지원하기 위해 자바에서는 부모 클래스 타입에 모든 자식 객체가 대입 될 수 있도록 자동 타입 변환을 시켜줍니다.
자동 타입 변환은 부모 클래스를 상속받은 자식클래스들의 타입을 부모 클래스 타입인 변수가 대입 받을 수 있습니다.
부모클래스 타입 변수 = new 자식클래스();
이 과정에서 자바는 자동적으로 부모클래스 타입의 변수가 자식클래스 타입의 인스턴스를 대입 받을 수 있도록 명시적으로 타입변환을 선언하지 않아도 내부적으로 타입변환을 시켜줍니다.
예를 들어 Animal 부모클래스를 상속받은 Cat 클래스의 인스턴스는 굳이 Cat 클래스 타입이 아니여도 상속 관계에 있는 부모클래스 타입의 변수라면, 대입 될 수 있습니다.
Animal animal = cat; 이 줄에서 자동적으로 타입 변환이 발생합니다.
그 관계를 JVM 메모리에선 어떻게 참조되는 지 위 그림에서 확인 할 수 있습니다.
이렇게 자동 타입 변환이 발생하여 자식클래스의 인스턴스를 대입 받은 부모클래스 타입의 변수는 몇 가지 특징을 가집니다.
1. 부모클래스에 선언된 필드와 메소드만 접근이 가능합니다.
-> 비록 변수는 자식 인스턴스를 참조하지만, 변수로 접근 가능한 멤버는 부모클래스 멤버로만 한정됩니다.
2. 만약 부모클래스 메서드를 자식클래스에서 오버라이딩 한 경우라면, 자식클래스 메서드가 대신 호출됩니다.
이 2가지 특징 중에 가장 중요한 것이 바로 2번 입니다.
부모클래스에서 미리 정의한 메서드 중에서 자식클래스가 오버라이딩하여 재정의 한 메서드가 대신 호출되는 것이야 말로 다형성의 효과를 극대화 할 수 있습니다.
예제를 보면서 필드의 다형성에 대해 더 알아보겠습니다.
해당 예제는 "이것이 자바다" 에서 발췌 했습니다.
Tire.java
package JavaDiversityExample;
public class Tire {
//필드
public int maxRotation; //최대 회전수 (타이어 수명)
public int accmulatedRotation; //누적 회전수
public String location; //타이어의 위치
//생성자
public Tire(String location, int maxRotation) {
//필드 초기화
this.location = location;
this.maxRotation = maxRotation;
}
//메서드
public boolean roll() {
++accmulatedRotation;//누적 회전수를 증가시킨다.
//누적회전수가 타이어의 최대회전수보다 많아지는 경우 타이어는 펑크난다.
if(accmulatedRotation < maxRotation) {
System.out.println(location + "Tire 수명 : " + (maxRotation - accmulatedRotation) + "회");
return true;
}else {
System.out.println("*** " + location + "Tire 펑크 ***");
return false;
}
}
}
Car.javapackage JavaDiversityExample;
public class Car {
//필드
Tire frontLeftTire = new Tire("앞 왼쪽", 6);
Tire frontRightTire = new Tire("앞 오른쪽", 2);
Tire backLeftTire = new Tire("뒤 왼쪽", 3);
Tire backRightTire = new Tire("뒤 오른쪽", 4);
//기본 생성자
//메서드
int run() {
System.out.println(">>> 자동차가 달립니다. >>>");
if(!frontLeftTire.roll()) {stop(); return 1;}
if(!frontRightTire.roll()) {stop(); return 2;}
if(!backLeftTire.roll()) {stop(); return 3;}
if(!backRightTire.roll()) {stop(); return 4;}
return 0;
}
void stop() {
System.out.println("xxx 자동차가 멈춥니다. xxx");
}
}
HanKooKTire.java
package JavaDiversityExample;
import javax.jws.soap.SOAPBinding;
public class HanKooKTire extends Tire{
public HanKooKTire(String location, int maxRotation) {
super(location, maxRotation);
// TODO Auto-generated constructor stub
}
//메소드
/** tire의 roll() 메서드 재정의 구현 **/
@Override
public boolean roll() {
++accmulatedRotation;
if(accmulatedRotation < maxRotation) {
System.out.println(location + " HanKooKTire 수명 : " + (maxRotation - accmulatedRotation) + "회");
return true;
}else {
System.out.println("*** "+ location +" HanKooKTire 평크 ***");
return false;
}
}
}
KumhoTire.java
package JavaDiversityExample;
public class KumhoTire extends Tire {
public KumhoTire(String location, int maxRotation) {
super(location, maxRotation);
// TODO Auto-generated constructor stub
}
//메소드
/** tire의 roll() 메서드 재정의 구현 **/
@Override
public boolean roll() {
++accmulatedRotation;
if(accmulatedRotation < maxRotation) {
System.out.println(location + " KumhoTire 수명 : " + (maxRotation - accmulatedRotation) + "회");
return true;
}else {
System.out.println("*** "+ location +" KumhoTire 평크 ***");
return false;
}
}
}
CarExample.java
package JavaDiversityExample;
public class CarExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car car = new Car();
for(int i=1; i<=5; i++) {
int problemLocation = car.run();
switch (problemLocation) {
case 1:
System.out.println("앞 왼쪽 HanKooKTire로 교체");
car.frontLeftTire = new HanKooKTire("앞 왼쪽 ", 15);
break;
case 2:
System.out.println("앞 오른쪽 KumhoTire로 교체");
car.frontRightTire = new KumhoTire("앞 오른쪽 ", 13);
break;
case 3:
System.out.println("뒤 왼쪽 HanKooKTire로 교체");
car.backLeftTire = new HanKooKTire("뒤 왼쪽 ", 14);
break;
case 4:
System.out.println("뒤 오른쪽 KumhoTire로 교체");
car.backRightTire = new KumhoTire("뒤 오른쪽 ", 17);
break;
default:
break;
}
System.out.println("=================" + i + " 회전 =================");
}
}
}
결과
>>> 자동차가 달립니다. >>>
앞 왼쪽Tire 수명 : 5회
앞 오른쪽Tire 수명 : 1회
뒤 왼쪽Tire 수명 : 2회
뒤 오른쪽Tire 수명 : 3회
=================1 회전 =================
>>> 자동차가 달립니다. >>>
앞 왼쪽Tire 수명 : 4회
*** 앞 오른쪽Tire 펑크 ***
xxx 자동차가 멈춥니다. xxx
앞 오른쪽 KumhoTire로 교체
=================2 회전 =================
>>> 자동차가 달립니다. >>>
앞 왼쪽Tire 수명 : 3회
앞 오른쪽 KumhoTire 수명 : 12회
뒤 왼쪽Tire 수명 : 1회
뒤 오른쪽Tire 수명 : 2회
=================3 회전 =================
>>> 자동차가 달립니다. >>>
앞 왼쪽Tire 수명 : 2회
앞 오른쪽 KumhoTire 수명 : 11회
*** 뒤 왼쪽Tire 펑크 ***
xxx 자동차가 멈춥니다. xxx
뒤 왼쪽 HanKooKTire로 교체
=================4 회전 =================
>>> 자동차가 달립니다. >>>
앞 왼쪽Tire 수명 : 1회
앞 오른쪽 KumhoTire 수명 : 10회
뒤 왼쪽 HanKooKTire 수명 : 13회
뒤 오른쪽Tire 수명 : 1회
=================5 회전 =================
Tire의 수명이 다 되기 전에는 기존에 있는 Tire의 roll() 메서드가 동작하지만,
타이어의 수명이 다 되면 Tire 타입에 Tire를 상속받은 KumhoTire, HanKooKTire클래스 인스턴스로 교체 시킵니다. 즉 부모클래스 Tire의 타입에 자식클래스 인스턴스를 대입 받는 것이지요.
이제 교체된 Tire에선 새로 대입 된 자식클래스 roll() 메서드가 동작합니다.
KumhoTire, HanKooKTire 클래스가 roll() 메서드를 오버라이딩해서 각자의 입맛에 맞게 재정의 했기 때문에 더 이상 부모클래스의 roll() 메서드가 동작하지 않고 재정의된 roll() 메서드가 호출되는 것이지요. 이것이 바로 필드의 다형성입니다.
다형성은 객체지향에서도 정말 중요한 개념입니다.
실무에서도 확장 가능하면서 다양한 결과를 내야할 때 반드시 다형성을 이용해야 합니다. 그렇지 않으면 유지보수는 과정이 너무 힘들기 때문이죠...
만약 A사의 카메라와 연동하여 셀카를 찍을 수 있는 안드로이드 앱을 개발 했다고 칩시다. 이 앱은 무조건 A사의 카메라만 연동이 가능하기 때문에 B사의 카메라와 연동하는 앱을 만들기 위해선 처음부터 다시 설계하고 개발 해야 합니다. 또한 이 제품을 가지고 영업을 할 때도 불리합니다.
왜냐면 우리는 A사 카메라가 아니고선 서비스가 되지 않습니다. 라는 말이거든요....
만약 확장 가능한 설계를 했다면, 우리 제품은 카메라 SDK만 지원한다면 어떤 카메라와도 연동할 수 있는 앱 입니다. 라고 영업할 수 있습니다. 느낌이 확 오시나요..? 그 만큼 다형성은 실무에서 더욱 중요한 개념이기에 꼭 곱씹으면서 완벽하게 알아둬야 합니다.
다음 시간에는 매개 변수의 다형성, 강제 타입 변환, 추상클래스 등에 대해서 알아보도록 하겠습니다.
'프로그래밍 > Java' 카테고리의 다른 글
| [Java] 다형성 (Polymorphism) (0) | 2017.10.25 |
|---|---|
| [Java] 객체 지향 설계란? (SOLID) (21) | 2017.08.24 |
| [Java] JVM WORA (Write Once Run Anywhere) (0) | 2017.08.17 |
| [Java] 많이 쓰는 String Methods (0) | 2017.08.15 |
| [Java] Immutable Class (불변 클래스) (2) | 2017.07.29 |
댓글