본문으로 바로가기

1. 다형성

  • 동일한 타입을 사용하지만 다양한 결과가 나오는 성질
  • 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있도록 해줍니다.
  • 다형성은 객체를 부품화가 가능하게 합니다. (하나의 타입 여러 객체 대입)
  • 다형성을 위해 자바에서는 부모 클래스로 타입 변환을 허용합니다.
  • 부모 타입에 모든 자식 객체가 대입될 수 있습니다.
  • 타입 변환이란 데이터 타입을 다른 데이터 타입으로 변환하는 행위를 말합니다.
  • 자식 타입은 부모 타입으로 자동 타입 변환이 가능합니다.

 

2. 자동 타입 변환(Promotion)

  • 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말합니다.
  • 자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급 가능합니다.
  • 부모가 아니더라도 상속 계층에서 상위 타입이라면 자동 타입 변환이 일어날 수 있습니다.
  • 부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능합니다.
  • 변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모 클래스 멤버로만 한정
  • 예외) 메소드가 자식 클래스에서 오버 라이딩되었다면 자식 클래스의 메소드가 대신 호출 (다양한 기능 구현 가능)

 

자동 타입 변환 예시

  • A 클래스를 상속 받은 B 클래스는 부모 타입인 A 타입으로 자동 변환 가능
  • B 클래스를 상속 받은 C 클래스는 상위 타입인 A 타입으로 자동 변환 가능

클래스 상속 예시 그림

B b = new B();
C c = new C();

A a1 = b; // B객체 A타입(부모)으로 자동 타입 변환
A a2 = c; // C객체 A타입(상위)으로 자동 타입 변환

 

부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능(부품화)

public class A {
    String fieldA;

    void methodA() {
        System.out.println("A 클래스의 메소드 입니다.");
        System.out.println("필드값: " + this.fieldA);
    }
}
public class B extends A {
    String fieldB;

    void methodB() {
        System.out.println("B 클래스의 메소드 입니다.");
    }
}
B b = new B();
C c = new C();

// B 객체를 A타입(부모)으로 자동 타입 변환
A a = b; 

// 부모 클래스의 필드와 메소드만 접근이 가능합니다.
a.fieldA = "B field";
a.methodA(); // A 클래스의 메소드 입니다. 필드값: B field

// 부모 타입으로 변경이 되면 자식 객체의 필드와 메소드에 접근이 불가능 합니다.
// a.fieldB = "B field"; (x)
// a.methodB(); (x)

 

메소드가 자식 클래스에서 오버 라이딩되었다면 자식 클래스의 메소드가 대신 호출(다양한 기능)

public class A {
    String fieldA;

    void methodA() {
        System.out.println("A 클래스의 메소드 입니다.");
    }
}
public class B extends A {
    String fieldB;

    @Override
    void methodA() {
        System.out.println("B 클래스의 메소드 입니다.");
    }
}
public class C extends B {
    String fieldC;

    @Override
    void methodA() {
        System.out.println("C 클래스의 메소드 입니다.");
    }
}
// B 객체 자동 타입 변환 및 메소드 호출(B 객체 메소드 오버라이딩)
A a = new B();
a.methodA(); // B 클래스의 메소드 입니다.

// C 객체 자동 타입 변환 및 메소드 호출(C 객체 메소드 오버라이딩) 
a = new C();
a.methodA(); // C 클래스의 메소드 입니다.

 

3. 강제 형 변환(Casting)

자식클래스 변수 = (자식클래스) 부모클래스타입;
  • 자식 타입이 부모 타입으로 자동 변환한 후, 다시 자식 타입으로 변환할 때 강제 타입 변환을 사용할 수 있습니다.
  • 자식 타입이 부모 타입으로 자동 변환하면, 부모 타입에 선언된 필드와 메소드만 사용 가능하다는 제약사항이 따르게 되는데 만약 자식 타입에 선언된 필드와 메소드를 꼭 사용해야 한다면 강제 타입 변환을 사용하면 됩니다.

 

예시

public class A {
    String fieldA = "A";

    void methodA() {
        System.out.println("A 클래스의 메소드 입니다.");
    }
}
public class B extends A {
    String fieldB = "B";

    void methodB() {
        System.out.println("B 클래스의 메소드 입니다.");
    }
}
// B 객체 자동 타입 변환
A a = new B();

// 필드 접근 및 메소드 호출, 자식 객체의 필드와 메소드 접근 불가
String fieldA = a.fieldA;
a.methodA();
// String fieldB = a.fieldB; (x)
// a.methodB(); (x)

// 자식 타입으로 강제 형 변환(Casting)
B b = (B) a;

// 자식 객체의 필드와 메소드 접근 가능
String fieldA2 = b.fieldA;
b.methodA();
String fieldB2 = b.fieldB;
b.methodB();

 

4. 필드의 다형성

  • 필드의 값을 다양화함으로써 실행 결과가 다르게 나오도록 구현
  • 필드의 타입은 변함이 없지만, 실행 도중에 어떤 객체를 필드로 저장하느냐에 따라 실행 결과가 달라질 수 있습니다. (다양한 기능)
  • 자식 클래스는 부모가 가지고 있는 필드와 메소드를 가지고 있으니 사용 방법은 동일할 것이고 자식 클래스는 부모의 메소드를 오버 라이딩해서 메소드의 실행 내용을 변경함으로써 다양한 실행 결과가 나오게 할 수 있습니다. (부품화)
  • 참조 객체만 변경하면 되므로 코드의 수정이 간결해집니다. 

 

예시

관계

 

Computer 클래스

public class Computer {
    private CPU cpu; // 필드의 다형성

    public Computer(CPU cpu) {
        this.cpu = cpu;
        cpu.cpuSpec(); // 객체 생성 시 CPU 스펙 출력
    }
}

 

Computer 클래스의 필드가 될 CPU 클래스

public class CPU {
    private String modelName;
    private int coreCnt;

    public CPU(String modelName, int coreCnt) {
        this.modelName = modelName;
        this.coreCnt = coreCnt;
    }

    public String getModelName() {
        return modelName;
    }

    public int getCoreCnt() {
        return coreCnt;
    }

    public void cpuSpec() {
        System.out.println(this.coreCnt);
        System.out.println(this.modelName);
    }
}

 

CPU 클래스를 상속받은 InterCpu 클래스

public class IntelCpu extends CPU {
    public IntelCpu(String modelName, int coreCnt) {
        super(modelName, coreCnt);
    }
    
    // 메소드 오버라이딩
    @Override
    public void cpuSpec() {
        System.out.println("---- Intel CPU STATUS ----");
        System.out.println("모델명: " + getModelName());
        System.out.println("코어 수: " + getCoreCnt());
        System.out.println("---- Intel CPU STATUS ----");
    }
}

 

CPU 클래스를 상속 받은 AmdCpu 클래스

public class AmdCpu extends CPU {
    public AmdCpu(String modelName, int coreCnt) {
        super(modelName, coreCnt);
    }

    // 메소드 오버라이딩
    @Override
    public void cpuSpec() {
        System.out.println("---- AMD CPU STATUS ----");
        System.out.println("모델명: " + getModelName());
        System.out.println("코어 수: " + getCoreCnt());
        System.out.println("---- AMD CPU STATUS ----");
    }
}

 

실행

// 객체 생성 (생생자에 전달 되는 객체는 부모 타입으로 자동 형 변환)
Computer computer = new Computer(new IntelCpu("intel-001", 8));

System.out.println();

// CPU 교체(객체 교체)
computer = new Computer(new AmdCpu("amd-001", 4));

 

출력 값

 

5. 매개 변수의 다형성

  • 자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 메소드를 호출할 때도 많이 발생
  • 매개 값을 다양화하기 위해 매개 변수에 자식 타입 객체를 지정할 수도 있습니다.
  • 매개 변수의 타입이 클래스일 경우, 해당 클래스의 객체뿐만 아니라 자식 객체까지도 매개 값으로 사용할 수 있습니다.

 

예시

Button 클래스

public class Button {
    public void action() {
        System.out.println("버튼 누름");
    }
}

 

Button 클래스를 상속 받은 OnButton 클래스

public class OnButton extends Button {
    @Override
    public void action() {
        System.out.println("ON 버튼 누름");
    }
}

 

Button 클래스를 상속 받은 OffButton 클래스

public class OffButton extends Button {
    @Override
    public void action() {
        System.out.println("OFF 버튼 누름");
    }
}

 

Button 객체를 사용하는 Action 클래스

public class Action {
    public void buttonAction(Button button) {
        button.action();
    }
}

 

실행

Action action = new Action();

OnButton onButton = new OnButton();
OffButton offButton = new OffButton();

// 매개 변수로 Button의 자식 객체 전달
action.buttonAction(onButton); // ON 버튼 누름
action.buttonAction(offButton); // OFF 버튼 누름

 

출력 값

출력 값