다형성이란 여러가지 형태를 가질 수 있는 능력을 의미하며 java에서는 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 만드는 것을 다형성이라고 한다.
예를 들어
class Tv{
boolean power;
int channel;
}
class SamsungTv extends Tv{
String text;
}
와 같이 Tv를 상속받은 SamsungTv calss가 존재할 경우
Tv tv = new Tv();
SamsungTv samsungTv = new SamsungTv();
Tv tv2 = new SamsungTv();
Tv라는 부모 클래스가 자손 클래스 SamsungTv 생성자를 통해 인스턴스를 생성하여 사용할 수 있다. 하지만 여기서 주의해야할 점은
이 점인데 java에서 이렇게 제한하는 이유는 생성하려는 인스턴스의 타입 class가 생성되는 인스턴스의 class보다 사용할 수 있는 멤버 변수의 개수가 더 적어야 하기 때문이다. 더 많다면 생성되는 인스턴스 class에서 부족한 멤버변수를 처리할 방법이 없다. 그렇기 때문에
SamsungTv samsungTv = new Tv();
와 같은 코드는 오류가 난다.
상속을 통해 부모 class와 자손 class가 정의 되었을 때 부모 class는 자손 class의 형태로도 변할 수 있다. 하지만 자손 class는 부모 class보다 가진게 더 많을 것으로 예상하기 때문에 자손은 부모가 될 수 없다.
기본형 변수와 같이 참조 변수도 형변환이 가능한데. 기본형 변수에서의 형변환과 동일하게 생각 하면 된다.
Tv tv = new Tv();
SamsungTv tv2 = new SamsungTv();
tv = tv2;
tv2 = (SamsungTv) tv;
자손 class는 부모 class가 될때 형변환을 따로 선언해주지 않아도 되지만 부모 class가 자손 class가 될때는 형변환을 선언해 주어야 한다. 이 점만 짚고 넘어가도 된다.
그럼 여기서 부모 class에 선언한 멤버 변수들과 메서드 그리고 자손 class에서 선언한 멤버 변수들과 메서드들을 어떻게 구분하고 사용할지 궁금해질 수 있다.
public class Test {
public static void main(String[] args){
Parent p = new Child();
Child c = new Child();
//부모
System.out.println("p = " + p.test);
p.test();
//자손
System.out.println("c = " + c.test);
c.test();
}
}
class Parent{
int test = 1;
public void test(){
System.out.println("test Parent = " + test);
}
}
class Child extends Parent{
int test = 2;
public void test(){
System.out.println("test Child = " + test);
}
}
위 코드를 실행했을 때의 결과 값이다. 이로써 알수 있는 것은 두 코드 모두 Child 인스턴스를 생성했지만 p는 Parent Type이기 때문에 Parent의 test값인 1이 출력되었고 c는 Child의 test값인 2가 출력되었다. 이렇게 멤버 변수의 이름이 중복되었을 때는 자신의 Type에 따라 멤버변수의 값을 가져오고 메서드의 경우 항상 자신의 인스턴스 Type에 맞춰서 실행함을 알수 있다.
또한 부모 class는 자손 class들을 매개변수와 배열에서도 다룰 수 있는데
먼저 매개변수의 경우
public class Test {
public static void main(String[] args){
write(new Tv());
write(new Computer());
}
public static void write(Product p){
System.out.println("가격 = " + p.price);
}
}
class Product{
int price;
public Product(int price) {
this.price = price;
}
}
class Tv extends Product{
public Tv() {
super(1000);
}
}
class Computer extends Product{
public Computer(){
super(2000);
}
}
위 코드로 알 수 있다. Product라는 부모 class를 두고 Tv와 Computer라는 자손 class를 생성하여 super() 생성자를 통해 가격을 결정하였다. 그리고 중요한 부분은 메서드의 매개변수를 정하는 부분인데. Tv와 Computer의 Type이 서로 다르기 때문에 오버로딩을 통해 매개변수의 타입을 정할 수도 있다. 하지만 이렇게 Product의 자손 class가 늘어날때마다 메서드를 오버로딩하여 정의한다면 너무 귀찮을 것이다. 그렇기 때문에 부모 class로 type을 정해놓고 매개변수로 받으면 그 자손 class는 모두 적용이 가능하다.
다음으로는 배열인데
public class Test {
public static void main(String[] args){
Product[] products = new Product[2];
products[0] = new Tv();
products[1] = new Computer();
for(Product p : products){
write(p);
}
}
public static void write(Product p){
System.out.println("가격 = " + p.price);
}
}
위 코드에서 main 부분만 수정하여 사용했다. 이렇게 Product라는 배열을 선언하여 Tv와 Computer class를 모두 받아 넣을 수 있고 실행시키면 위와 같은 결과가 나온다.
다형성을 통해 java는 객체를 더 확장성 있고 다양한 형태로 사용할 수 있음을 이해하자