이것이 자바다 의 Chapter08 을 참고하여 정리한 포스트입니다.

이전글
인터페이스 이론정리 - 1
인터페이스 이론정리 - 2
인터페이스 이론정리 - 3

Chapter 08. 인터페이스 - (4)

5. 타입 변환과 다형성

다형성 : 하나의 타입에 대입되는 객체에 따라서 실행 결과가 다양한 형태로 나오는 성질

인터페이스의 다형성 : 인터페이스를 사용해서 메소드를 호출하면, 쉽게 구현 객체를 교체함으로써 실행 결과가 다양해진다.
인터페이스 타입에 어떤 구현 객체를 대입하느냐에 따라 실행 결과가 달라진다.

  • ex) 프로그렘의 X 인터페이스를 구현한 클래스 클래스로 클래스 A를 선택한다.
    그러나 클래스 A에 문제가 발생하여 I 인터페이스를 구현한 클래스 B를 만들고 코드의 한 줄만 수정해서 프로그램을 재실행할 수 있다.
    1
    2
    3
    4
    5
    6
    7
    8
    
      //프로그램 코드
    
      //X x = new A(); // 수정
      X x = new B();
    
      // 메소드는 수정이 필요 없다.
      x.method1();
      x.method2();
    
  • A 클래스(객체)와 B 클래스(객체) 를 인터페이스 X 의 구현 클래스로 작성
    1
    2
    3
    4
    
      interface x{
          void method1();
          void method2();
      }
    

인터페이스 매개 변수의 다형성 : 인터페이스 타입으로 매개 변수를 선언하면 메소드 호출 시 매개값으로 여러 가지 종류의 구현 객체를 사용할 수 있다. 즉, 메소드의 실행 결과가 다양하게 나온다.

  • ex) useRemoteControl() 메소드의 매개 변수가 RemoteControl 인터페이스 타입이다.
    매개값으로 Television 객체 또는 Audio 객체를 선택적으로 줄 수 있다.
    1
    2
    
      public void useRemoteControl( RemoteControl rc) {...}
      // Television 구현 객체 또는 Audio 구현 객체를 매개값으로 사용 가능
    

5.1 자동 타입 변환(Promotion)

구현 객체가 인터페이스 타입으로 변환되는 것이 자동 타입 변환이다.

1
2
//구현 객체 → 인터페이스 변수로 자동 타입 변환
인터페이스 변수 = 구현객체;
  • 인터페이스 구현 클래스를 상속해서 자식 클래스로 만들었다면 자식 역시 인터페이스 타입으로 자동 타입 변환시킬 수 있다.
    1
    2
    3
    4
    
      B b = new B();
      C c = new C();
      D d = new D();
      E e = new E();
    

    1
    2
    3
    4
    
      A a1 = b;   //가능
      A a2 = c;   //가능
      A a3 = d;   //가능
      A a4 = e;   //가능
    

    자동 타입 변환을 이용하면 필드의 다형성과 매개 변수의 다형성을 구현할 수 있다.
    필드와 매개 변수 타입을 인터페이스로 선언하면 다양한 구현 객체를 대입하여 실행 결과를 다양하게 출력할 수 있다.

5.2 필드의 다형성

상속에서의 다형성과 비슷하다. 상속에서는 어떤 클래스 타입에 자식 객체를 대입해서 교체할 수 있다.

인터페이스는 어떤 인터페이스의 구현 클래스들은 그 인터페이스로 동일하게 사용할 수 있어 교체가 가능한 객체이다.
공통적으로 인터페이스를 구현했기 때문에 인터페이스 내에 있는 메소드를 가지고 있기 때문이다.

  • (예시) 자동차 설계 시 필드 타입으로 타이어 인터페이스를 선언하게 되면 필드값으로 한국 타이어 또는 금호 타이어 객체를 대입할 수 있다. (자동 타입 변환)
    1
    2
    3
    4
    5
    6
    
      public class Car {
          Tire frontLeftTire = new HankookTire();
          Tire frontRightTire = new HankookTire();
          Tire backLeftTire = new HankookTire();
          Tire backRightTire = new HankookTire();
      }
    
  • Car 객체를 생성, 초기값으로 대입한 구현 객체 대신에 다른 구현 객체를 대입할 수 있다.
    1
    2
    3
    
      Car myCar = new Car();
      myCar.frontLeftTire = new KumhoTire();
      myCar.frontRightTire = new KumhoTire();
    
  • Car 객체의 run() 메소드에서 타이어 인터페이스에 선언된 roll() 메소드를 호출한다.
    1
    2
    3
    4
    5
    6
    
      void run() {
          frontLeftTire roll();
          frontBackTire roll();
          backLeftTire roll();
          backRightTire roll();
      }
    

    KumhoTire 로 교체된 후에는 KumhoTire 객체의 roll() 메소드가 호출된다.
    Car 의 run() 메소드 수정 없이 다양한 roll() 실행 결과를 얻을 수 있다. (필드의 다형성)


  • ex) Tire.java 인터페이스
    1
    2
    3
    
      public interface Tire {
          public void roll(); //roll() 메소드 호출 방법 설명
      }
    
  • HankookTire.java 구현 클래스
    1
    2
    3
    4
    5
    6
    7
    
      public class HankookTire implements Tire {
          //Tire 인터페이스 구현
          @Override
          Public void roll() {
              System.out.println("한국 타이어가 굴러갑니다.");
          }
      }
    
  • KumhoTire.java 구현 클래스
    1
    2
    3
    4
    5
    6
    7
    
      public class KumhoTire implements Tire {
          //Tire 인터페이스 구현
          @Override
          Public void roll() {
              System.out.println("한국 타이어가 굴러갑니다.");
          }
      }
    
  • Car.java 필드 다형성
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      public class Car {
          //인터페이스 타입 필드 선언과 초기 구현 객체 대입
          Tire frontLeftTire = new HankookTire();
          Tire frontRightTire = new HankookTire();
          Tire backLeftTire = new HankookTire();
          Tire backRightTire = new HankookTire(); 
    
          void run() {
          //인터페이스에서 설명된 roll() 메소드 호출
              frontLeftTire.roll();
              frontRightTire.roll();
              backLeftTire.roll();`
              backRightTire.roll();
          }
      }
    
  • CarExample.java 필드 다형성 테스트
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
      public class Car {
          Car myCar = new Car();
    
          myCar.run();
    
          myCar.frontLeftTire = new KumhoTire();
          myCar.frontRightTire = new KumhoTire();
    
          myCar.run();
      }
    
    결과
    한국 타이어가 굴러갑니다.
    한국 타이어가 굴러갑니다.
    한국 타이어가 굴러갑니다.
    한국 타이어가 굴러갑니다.
    금호 타이어가 굴러갑니다.
    금호 타이어가 굴러갑니다.
    한국 타이어가 굴러갑니다.
    한국 타이어가 굴러갑니다.

5.3 인터페이스 배열로 구현 객체 관리

위에서는 Car 클래스에서 4개의 타이어 필드를 인터페이스로 선언했지만 인터페이스 배열로도 관리할 수 있다.

1
2
3
4
5
6
Tire[] tires = {
    new HankookTire(),
    new HankookTire(),
    new HankookTire(),
    new HankookTire()
};

tire 배열의 각 항목은 Tire 인터페이스 타입이다.
구현 객체인 KumhoTire를 대입하면 자동 타입 변환이 발생한다.

1
tire[1] = new KumhoTire();
1
2
3
4
5
void run() {
    for(Tire tire : tires) {
        tire.roll();
    }
}
  • ex) 이전 예제에서 작성한 Car 클래스의 타이어 필드를 배열로 수정
    Car.java 필드 다형성
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      public class Car {
          Tire[] tires = {
              new HankookTire(),
              new HankookTire(),
              new HankookTire(),
              new HankookTire()
          };
    
          void run() {
              for(Tire tire : tires) {
                  tire.roll();
              }
          }
      }
    
  • CarExample.java 필드 다형성 테스트
    1
    2
    3
    4
    5
    6
    7
    8
    
      Car my car = new Car();
    
      myCar.run();
    
      myCar.tires[0] = new KumhoTire();
      myCar.tires[1] = new KumhoTire();
    
      myCar.run();
    

    결과는 수정 전 위의 CarExample.java 의 결과와 동일하다.

5.4 매개 변수의 다형성

자동 타입 변환은 필드의 값을 대입할 때 발생하지만, 주로 메소드를 호출할 때 많이 발생한다.
매개값을 다양화하기 위해서 상속에서는 매개 변수를 부모 타입으로 선언하고 호출할 때에는 자식 객체를 대입했다.

매개 변수를 인터페이스 타입으로 선언하고 호출할 때에는 구현 객체를 대입한다.

  • ex) Driver 클래스에 정의된 drive() 메소드가 있는데 Vehicle 타입 매개 변수가 선언
    1
    2
    3
    4
    5
    
      public clas Driver {
          public void drive(Vehicle vehicle) {
              vehicle.run();
          }
      }
    
  • Vehicle 인터페이스 타입은 다음과 같다.
    1
    2
    3
    
      public interface Vehicle {
          public void run();
      }
    
  • 만약 Bus 가 구현 클래스라면 Driver 의 driver() 메소드를 호출할 때 Bus 객체를 생성해서 매개값으로 줄 수 있다.
    1
    2
    3
    
      Driver driver = new Driver();
      Bus bus = new Bus();
      driver.drive(bus);   //자동 타입 변환 발생 
    
  • drive() 메소드는 Vehicle 타입을 매개 변수로 선언, but Vehicle 을 구현한 Bus 객체가 매개값으로 사용되면 자동 타입 변환이 발생한다.
    1
    
      Vehicle vehicle = bus;  ///bus -> vehicle 자동 타입 변환
    

매개 변수의 다형성
매개 변수의 타입이 인터페이스일 경우, 어떠한 구현 객체도 매개값으로 사용할 수 있고, 어떤 구현 객체가 제공되는냐에 따라 메소드의 실행 결과는 다양해질 수 있다.

1
2
3
void drive(Vehicle vehicle) {   // vehicle : 구현 객체
    vehicle.run();  // 구현 객체의 run() 메소드가 실행됨
}

  • Driver.java 매개 변수의 인터페이스화
    1
    2
    3
    4
    5
    
      public class Driver {
          public void drive(Vehicle vehicle) {
              vehicle.run();
          }
      }
    
  • Vehicle.java 인터페이스
    1
    2
    3
    
      public interface Vehicle {
          public void run();
      }
    
  • Bus.java 구형 클래스
    1
    2
    3
    4
    5
    6
    
      public class Bus implements Vehicle {
          @Override
          public void run() {
              System.out.println("버스가 달립니다.");
          }
      }
    
  • Taxi.java 구현 클래스
    1
    2
    3
    4
    5
    6
    
      public class Taxi implements Vehicle {
          @Override
          public void run() {
              System.out.println("택시가 달립니다.");
          }
      }
    
  • DriverExample.java 매개 변수의 다형성 테스트
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
      public class DriverExample {
          public static void main(String[] args) {
              Driver driver = new Driver();
    
              Bus bus = new Bus();
              Taxi taxi = new Taxi();
    
              //자동 타입 변환
              driver.drive(bus);  // Vehicle vehicle = bus;  
              driver.drive(taxi); // Vehicle vehivle = taxi;
          }
      }
    
    결과
    버스가 달립니다.
    택시가 달립니다.

5.5 강제 타입 변환(Casting)

제약 사항 : 구현 객체가 인터페이스 타입으로 자동 변환하면, 인터페이스에 선언된 메소드만 사용 가능하다.

  • ex) 인터페이스에 있는 세 개의 메소드 선언, 클래스에는 다섯 개의 메소드 선언
    => 인터페이스 호출 가능한 메서드는 세 개
  • 구현 클래스에 선언된 필드와 메소드를 사용해야 할 경우도 발생할 시 강제 타입 변환을 사용한다.
    (구현 클래스 타입을 변환하여 구현 클래스의 필드와 메소드 사용)
  • 강제 타입 변환은 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능하다.
  • Vehicle.java 인터페이스
    1
    2
    3
    
      public interface Vehicle {
          public void run();
      }
    
  • Bus.java 구현 클래스
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
      public class Bus implements Vehicle {
          @Override
          public void run() {
              System.out.println("버스가 달립니다.");
          }
    
          public void checkFare() {
              System.out.println("승차요금을 체크합니다.");
          }
      }
    
  • VehicleExample.java 강제 타입변환
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      public class VehicleExample {
          public static void main(String[] args) {
              Vehicle vehicle = new Bus();
    
              vehicle.run();
              //vehicle.checkFare(); 
              //↑ (x) Vehicle 인터페이스에는 CheckFare() 가 없다.
    
              Bus bus = (Bus) vehicle;    //강제 타입 변환
    
              bus.run();
              bus.checkFare();    //Bus 클래스에는 checkFare()가 있다.
          }
      }
    

5.6 객체 타입 확인(instanceof)

강제 타입 변환 시에 어떤 구현 객체가 변환되어 있는지 알 수 없는 상태에서 무작정 변환할 시 ClassCastException 이 발생한다.

  • ex) Taxi 객체가 인터페이스로 변환되어 있지만 Bus 타입을 강제 타입변환
    => ClassCastException 발생 (클래스 타입이 다름)
    1
    2
    
      Vehicle vehicle = new Taxi();
      Bus bus = (Bus) vehicle;
    
  • 메소드의 매개 변수가 인터페이스로 선언될 시 매개 변수의 다형성으로 어떤 구현 객체가 지정될 시 모른다. 아래의 예시와 같이 매개값을 Bus로 강제 변환하면 ClassCastException 발생할 수 있다.
    1
    2
    3
    4
    5
    
      public void drive(Vehicle vehicle) {
          Bus bus = (Bus) vehicle;
          bus.checkFare();
          vehicle.run();
      }
    
반드시 매개값이 어떤 객체인지 instanceof 연산자로 확인하고 강제 타입 변환해야 하는 경우
인터페이스 타입으로 자동 변환된 매개값을 메소드 내에서 다시 구현 클래스 타입으로 강제 타입 변환해야 할 때이다.
  • Driver.java 객체 타입 확인
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
      public class Driver {
          public void drive(Vehicle vehicle) {    //vehicle ← Bus, Taxi 객체
    
              //Bus가 vehicle 매개변수가 참조하는 객체인지 조사
              if(vehicle instanceof Bus) { 
                  Bus bus = (Bus) vehicle;
                  bus.checkFare();    //Bus 타입으로 강제타입 변환하는 이유
              }
              vehicle.run();
    
          }
      }
    

🌞 정보 : 공부 기록용 블로그입니다. 오타나 내용 오류가 있을 경우 알려주시면 감사하겠습니다.

댓글남기기