다형성
1-1. 다형성의 정의:
상속을 전제로 한 기술로써, 자식 객체를 부모 클래스 타입의 변수에 담아 제어하는 것이다. 이제까지 우리는 클래스명 참조변수병 = new연산자 생성자명(클래스 명); 의 양식으로 객체의 인스턴스화를 진행해 왔다. 다형성을 활용하면 상속의 상위클래스명 = 참조변수명 new연산자 생성자명(클래스명); 의 방식으로 작성 및 접근이 가능하다.
1-2. 다형성의 활용 조건: 이는 부모-자식간의 상속관계로 엮인 클래스끼리만 가능하며, 모든 클래스의 상위 클래스인 Object 클래스와도 가능하다. 부모 타입의 참조 변수는 자식 객체 메소드에 접근할 수 없으나 자식 타입으로 다시 형변환하면 자식 메소드도 사용이 가능하다.
1-3. 형변환에는 2가지 종류가 있다:
1-3-1. Upcasting:자식 클래스에서 부모 타입으로 자동 형변환.
1-3-2: Downcasting: 부모에서 자식으로 형변환 (명시적 형변환 필요)
ex. Child ch = (Child)parent;
1-4. instanceof 연산자:
a instanceof A : a가 A타입의 객체인가? 를 판별(boolean: true/false 리턴)
instanceof 연산 결과가 true이면 A타입으로의 형변환을 보장한다. 따라서, 상속 관계에서의 부모/자식 클래스를 형변환 할 때에는 반드시 instanceof 연산자를 거쳐서 진행해 주는 것이 좋다.
1-5. 상속의 범위 개념:
자식 클래스는 부모 클래스로부터 데이터를 상속 받으며, 자식 클래스만의 독립적인 데이터를 더 해 데이터를 확장(extends)해 나가므로 자식 클래스의 범위 안에 부모 클래스가 들어 있다고 생각할 수 있다.
1-6. ClassCastExeption:
부모/자식간의 관계가 아닌 클래스끼리 형변환을 시도하는 경우 나는 에러.
따라서 instanceof 연산자를 사용하여 형변환 가능 여부를 미리 파악해야 한다.
1-7. 다형성을 통한 객체 배열 초기화:
Parent[] arr = new Parent[2];
arr[0] = new Son();
arr[1] = new Daughter();
//부모클래스배열[] 배열명 = new연산자 부모생성자(=클래스명)[배열 크기];
//배열 arr[인덱스0] = new연산자 아들클래스();
//배열 arr[인덱스1] = new연산자 딸클래스();
1-8. 자식 객체를 부모 객체에 담을 수 있으나, 부모 객체는 자식 객체에 담을 수 없다. 또한 비상속 관계이거나 형제 관계의 클래스들 역시 서로를 담을 수 없다.
1-9. 매개변수에서 다형성 활용하기: action이라는 파라미터 메소드를 활용하고자 할 때 반드시 객체 각자의 참조 주소값을 각각 선언하지 않아도 된다. 파라미터로 쓰일 클래스들이 어떠한 상위 클래스와 부모-자식 관계로 연결되어 있다면, 이 부모 타입의 참조주소를 선언하는 것 만으로도 사용이 가능해진다.
//ex
Daughter d = new Daughter();
Son s = new Son();
Parent p = new parent();
action(d); //이렇게,
action(s); //각자 선언할 필요 없이
action(p); //부모타입의 참조 변수를 파라미터로도 정상 작동한다.
또한, 위에서처럼 부모 타입의 참조 변수를 파라미터로 대입하고, 자식 클래스 각자의 고유한 메소드를 발동시키고 싶은 경우 객체 형변환을 해 주면 된다. 상기했듯 객체 형변환의 경우는 반드시 instanceof 연산자를 사용하여 형변환이 가능한 상황인지의 판별을 선행해 주는 것이 좋다.
public void action(Parent p) {
if(a instanceof Daughter && a instanceof Son) {
((Son)a).punch();
((Daughter)a).kick();
}
1-10. 리턴 타입에서의 다형성 활용하기:
자식 객체를 부모 타입의 리턴 객체로 보낼 수 있다.
//
public void test5() {
Parent p1 = getParent();
Parent p2 = getParent();
}
public Animal getParent() {
Random rnd = new Random();
return rnd.nextBoolean() ? new Son() : new Daughter();
} //삼항연산자를 통해서 A or B 리턴하기
1-11. 항상 자식 객체가 부모 객체의 확장형(extends)임을 유의하라.
Parent p = new Child();
c.methodinParent(); <-가능
c.methodinChild(); <-불가
1-12. 동적 바인딩: 자식 클래스가 부모 메소드를 오버라이딩 한 경우에만 부모 타입의 참조변수로 해당 메소드를 호출하면 부모 타입의 메소드(정적 바인딩)가 아닌 자식클래스의 오버라이드 된 메소드를 호출한다.(동적 바인딩)
<공식 요약>
선언
상위클래스명 = 참조변수명 new연산자 생성자명(클래스명);
Parent = p new Daughter();
Parent = p new Son();
객체 배열 초기화
Parent[] arr = new Parent[2];
arr[0] = new Son();
arr[1] = new Daughter();
//부모클래스배열[] 배열명 = new연산자 부모생성자(=클래스명)[배열 크기];
//배열 arr[인덱스0] = new연산자 아들클래스();
//배열 arr[인덱스1] = new연산자 딸클래스();
파라미터로 부모 객체의 참조변수 사용
Parent p = new parent();
action(p);
instanceof 판별 후 형변환
public void action(Parent p) {
if(a instanceof Daughter && a instanceof Son) {
((Son)a).punch();
((Daughter)a).kick();
}