Programming/Java

다형성

helloworld: 2021. 8. 2. 11:04

객체지향

프로그램을 객체들 간 메시지를 주고받고 상호작용하는 협력 관계로 설계/바라보는 관점

객체지향 특징

추상화, 캡슐, 상속, 다형성

 

다형성

참조변수의 다형성

  • 상속 관계/개념으로부터 다형성이 나온다
  • 조상클래스 타입 참조변수로 여러 자손 타입 인스턴스를 참조할 수 있는 것
  • 같은 타입의 인스턴스라도, 참조변수 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다. (참고 : 모든 참조변수는 null 또는 4byte 주소값이 저장되며, 참조변수의 타입은 참조할 수 있는 객체의 타입과 사용할 수 있는 멤버의 수를 결정한다.)
  • 자손타입 참조변수로 조상타입 인스턴스를 참조할 수는 없다.
    • 이유
      • 자손타입 참조변수로 조상타입 인스턴스를 참조하면 존재하지 않는 멤버를 사용할 가능성이 있으므로

               ※ 클래스는 상속을 통해 확장은 가능하나, 축소되지는 않는다.

                   -> 조상 인스턴스의 멤버 개수는 자손 인스턴스의 멤버 개수보다 항상 적거나 같다.

               ※ 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스 멤버 개수보다 같거나 적어야 한다.

 

다형성의 필요성

  • 매개변수의 다형성

      조상클래스 타입 매개변수로 자손타입 참조변수를 매개변수로 받을 수 있다.

 

  • 여러 종류의 객체를 배열로 다루기

      조상타입 참조변수 배열로 여러 자손 타입 객체를 묶어서 다룰 수 있다.

 

역할과 구현의 관점에서의 다형성

역할과 구현을 분리함으로써 실행 시점에 인스턴스를 유연하게 변경할 수 있다

역할과 구현 -> 다형성을 활용한 스프링의 핵심 기술 DI 컨테이너.. IoC 제어의 역전, DI 의존관계 주입 이용해 역할과 구현 분리 지원

 

참조변수의 형변환

1. 참조변수의 형변환은 서로 상속관계에 있는 클래스 사이에서만 가능하다.

  • 업캐스팅 : 자손 타입 -> 조상 타입   ※ 형변환 생략 가능
  • 다운캐스팅 : 조상 타입 -> 자손 타입  ※ 형변환 생략 불가

※ 업캐스팅에서 형변환 생략 가능한 이유

   참조변수가 다룰 수 있는 멤버의 개수가 인스턴스가 가진 멤버의 개수보다 분명히 적으므로

 

※ 다운캐스팅에서 형변환 생략 불가한 이유

  • 참조변수가 다룰 수 있는 멤버의 개수가 인스턴수의 멤버 개수보다 더 많아지므로 존재하지 않는 멤버를 다루는 문제가 발생할 여지가 있다. 따라서 형변환 생략 불가하다.
  • instanceof 연산자를 이용해 참조변수가 참조하고 있는 실제 인스턴스의 타입을 먼저 확인하는 것이 안전하다.

 

2. 형변환은 참조변수의 타입을 변환하는 것이지, 인스턴스를 변환하는 것은 아니다. 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다. 단지 형변환을 통해 인스턴스에서 사용할 수 있는 멤버 개수를 조절하는 것뿐이다.

※ 예제소스

    *** 아래의 예제소스에서 FireEngine은 Car의 자손타입이다.

class CastingTest2 {
   public static void main(String args[]) {
   
      Car car = new Car();
      Car car2 = null;
      FireEngine fe = null;
      
      
      car.drive();
      fe = (FireEngine)car; //컴파일 ok 그러나 실행 시 classCastException
      fe.drive();
      car2 = fe;
      car2.drive();
   }
}

※ 이 예제소스에서 실행 시 classCastException 에러가 나는 이유는?

    car 참조변수가 참조하고 있는 인스턴스가 Car 타입이기 때문이다.

    즉, 자손타입 참조변수로 조상타입 인스턴스를 참조할 수 없기 때문이다.

 

 

다형성의 활용 - instanceof 연산자

  • 참조변수가 참조하는 인스턴스의 타입을 확인하는 용도로 주로 조건문에서 사용한다.
  • 결과로 true or false를 반환한다.
  • true일 경우 형변환을 해도 문제가 없다는 뜻이다.
  • 참조변수 instanceof 클래스명

 

참조변수와 인스턴스의 연결

  • 자손클래스에서 조상 클래스의 멤버와 같은 이름으로 중복되는 멤버가 없는 경우 참조변수 타입에 따른 변화가 없다.
class BindingTest {
	public static void main(String[] args) {
    
    	Parent p = new Child();
        Child c = new Child();
        
        System.out.println("p.x = " + p.x);  //p.x = 100
        p.method();                          //Parent Method
        
        System.out.println("c.x = " + c.x);  //c.x = 100
        c.method();                          //Parent Method
       }
}

class Parent {
	int x = 100;
    void method() {System.out.println("Parent Method");}
}

class Child extends Parent() {}

 

  • 자손클래스에서 조상 클래스의 멤버변수와 같은 이름으로 멤버변수를 중복해서 정의한 경우는 참조변수 타입에 따라 결과가 달라진다.
    class BindingTest {
    	public static void main(String[] args) {
        	Parent p = new Child();
            Child c = new Child();
            
            System.out.println("p.x = " + p.x);  //p.x = 100
            p.method();                          //x = 200
                                                 //super.x = 100
                                                 //this.x = 200
            System.out.println();
            System.out.println("c.x = " + c.x);  //c.x = 200
                                                 //x = 200
                                                 //super.x = 100
                                                 //this.x = 200
            c.method();
        }
    }
    
    class Parent {
    	int x = 100;
        
        void method() {
        	System.out.println("Parent Method");
        }
    }
    
    class Child extends Parent{
    	int x = 200;
        
        void method() {
        	System.out.println("x = " + x);
            System.out.println("super.x = " + super.x);
            System.out.println("this.x = " + this.x);
        }
    }​
     
    • 인스턴스 메서드 : 참조변수의 타입에 관계 없이 항상 인스턴스의 메서드가 호출
    • 인스턴스 변수 : 참조변수의 타입에 따라 달라진다.

      ※ static메서드는 참조변수의 타입에 영향을 받는다. 따라서 반드시 '클래스명.메서드()'로 호출해야 한다.

      참조변수 타입에 영향을 받지 않는 것은 인스턴스 메서드뿐.