다형성은 동일한 코드로 다양한 타입의 객체를 처리하는 기법을 의미함. 넓은 의미에서는 메소드 오버로딩, 오버라이딩, 제네릭 프로그래밍도 다형성에 포함된다.
업캐스팅
예시
class Shape {
protected int x, y;
public void draw() {
System.out.println("Shape Draw");
}
}
class Rectangle extends Shape {
private int width, height;
public void draw() {
System.out.println("Rectangle Draw");
}
}
public class PolyMorphism {
public static void main(String[] args) {
Shape s1, s2;
s1 = new Shape(); //1
s2 = new Rectangle(); //2
}
}
그렇다면 2와 같이 부모 클래스 변수로 자식 클래스 객체를 참조하였을 경우, s2를 통하여 자식 클래스의 모든 필드와 메소드를 사용할 수 있을까? 그렇지는 않다. 자식 클래스 중에서 부모 클래스로부터 상속받은 부분만을 s2를 통해서 사용할 수 있고, 나머지는 사용하지 못한다.
=> 자식 클래스 안에 부모 클래스가 포함되기 때문에(=자식 클래스가 더 큰 범위이기 때문에), 부모 클래스 변수로 자식 클래스 객체를 참조할 수는 있지만, 부모 클래스 변수는 부모 클래스에 있는 부분만을 사용할 수 있음. 자식 클래스에서 추가된 부분은 사용하지 못함.
위에서 조금 더 변형된 코드를 살펴보자.
public class PolyMorphism {
public static void main(String[] args) {
Shape s = new Rectangle();
Rectangle r = new Rectangle();
s.x = 0; //1
s.y = 0; //2
s.width = 100; //3
s.height = 100; //4
}
1, 2 => s가 Shape의 변수이기 때문에 Shape 클래스의 필드(int x, int y)와 메소드에 접근할 수 있음. 오류 나지 않음
3,4 => 컴파일 오류 발생. s는 Shape의 변수이기 때문에 Rectangle 클래스의 필드(width, height)와 메소드에 접근할 수 없다.
그렇지만 r을 통해서는 모든 필드를 전부 사용할 수 있다. r이 자식 클래스의 객체고, 자식 클래스는 부모 클래스를 포함하고 있는, 자식 클래스가 더 큰 개념이기 때문임. 부모 클래스는 자식 클래스가 무엇을 추가하였는지 알 수 있는 방법이 없기 때문.
기억해야 할 것은 어떤 메소드나 필드를 사용할 수 있는지는 변수의 타입(부모 클래스 변수인지, 자식 클래스 변수인지)에 의하여 결정된다는 것
다운캐스팅
업캐스팅: 자식 객체를 부모 참조 변수로 참조하는 것. 묵시적으로 수행될 수 있다.
다운캐스팅: 부모 객체를 자식 참조변수로 참조하는 것이다. 명시적으로 하여야한다.
class Parent {
void print() {
System.out.println("Parent 메소드 호출");
}
}
class Child extends Parent {
void print() {
System.out.println("Child 메소드 호출");
}
}
public class Casting {
public static void main(String[] args) {
Parent p = new Child();
p.print();
//Child c = new Parent();
Child c = (Child)p;
c.print();
}
}
Parent p = new Child();
p.print();
=> 업캐스팅. 자식 객체(Child)를 부모 객체(Parent)로 형변환. p.print(); - 자식 메소드 호출.
//Child c = new Parent();
=> 자식 클래스 변수(c)를 부모 객체(Parent)로 형변환하려고 했으나, 다운캐스팅은 묵시적으로 불가능하기 때문에 오류.
Child c = (Child)p;
c.print();
=> 다운캐스팅. 부모 객체(p)를 자식 객체(Child)로 형변환. 메소드 오버라이딩으로 부모 객체 무효화돼서, 자식 객체의 print() 호출.