다형성(Polymorphism)

임준철·2021년 4월 11일
0

OOP

목록 보기
6/6

다형성이란?

  • 하나의 객체가 여러가지 타입을 가질 수 있는 것을 말한다.
  • 자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를
    참조할 수 있도록 하여 구현하고 있다.
  • 다형성은 상속, 추상화와 더불어 객체지향 프로그래밍을 구성하는 중요한 특징 중 하나이다.
  • 객체지향 프로그래밍의 유연성, 재활용성, 유지보수성에 기본이 되는 특징이다.

다형성의 장점

  • 다양한 여러 클래스를 하나의 자료형(상위 클래스)으로 선언하거나 형변환 하여
    각 클래스가 동일한 메소드를 오버라이딩 한 경우, 하나의 코드가 다양한 구현을 실행 할 수 있다.
  • 유사한 클래스가 추가되는 경우 유지보수에 용이하고, 각 자료형 마다 다른 메소드를 호출하지 않으므로 코드에서 많은 if문이 사라진다.

다형성의 다양한 특징

  • 오버로딩에 의한 다형성, 오버라이딩에 의한 다형성(상속에 의한 다형성과 같다.)

  • 부모 클래스 타입으로 자식 클래스 객체를 참조하는 특징

  • 부모 클래스로 자식 클래스를 참조한 경우, 자식 클래스의 메소드는 사용할 수 없다.

  • 반대로 자식 클래스 타입으로 부모 클래스 타입의 객체를 참조할 경우에는 문법적으로 오류는 나지 않지만, 런타임시 에러가 발생한다.

    class Foo{
       public void methodA(){
       }
    }
    class Bar extends Foo{
       public void methodB(){
       }
    }
    public class Poly01 {
       public static void main(String[] args) {
           Bar bar = new Bar(); //new가 있어야 새로운 객체를 만듬 자식 객체를 생성한 것이다.
           Foo foo = (Foo)bar; // 부모 클래스 타입으로 자식 객체를 받음.
           // 여전히 힙영역에는 자식객체가 있는 것이다.
    
           foo.methodA(); // Foo객체로받아서 문법적으로 a만 사용 가능, b는 불가능
           //foo.methodB();//이 경우 자식 클래스 메소드는 사용이 불가능 문법적으로 불가능함. 문법적으로 제한을 한 것이다.
    
           Foo foo1 = new Foo();
           
           //Bar bar1 = (Bar)foo1; 
           // 자식 클래스 타입으로 부모 객체를 받음 이거 자체가 오류(문법 오류는 아님)
           // 문법 오류는 아니지만, 런타임 오류가 발생
           // 자식클래스 자료형으로 객체를 보려 하지만, 부모 객체 부분만 있기 때문에
           // 런타임에서 오류가 발생시킴.
           //bar1.methodA(); //문법오류x 성립하지 않음
          // bar1.methodB(); //문법오류x 성립하지 않음
    
           Bar bar1 = (Bar)foo; 
           // 원래 자식객체를 자식객체로 캐스팅함 자식클래스 타입으로 자식 객체를 받음
           // 힙 영역에는 계속 자식 객체가 있었던 것. 부모객체 자료형으로 있는것을 자료객체로 캐스팅할 때
           // foo변수에 자식객체가 담겨져있는지 부모객체가 담겨져있는지 문법적으로 알수없음
           bar1.methodA();
           bar1.methodB();
       }
    }
  • 자식클래스로 부모 클래스를 참조하려 하면 java.lan.ClassCastException 오류 발생
 public class Main {
     public static void main(String args[]) {
         Foo foo = new Foo();
         Bar bar;
 
         // bar = (Bar)foo; // error
         if (foo instanceof Bar) { // returns false
             bar = (Bar)foo;
         }
     }
 }

형변환과 오버라이딩 메소드 호출

  • 멤버 변수의 재정의는 선언된 객체의 타입을 따른다. (문법적으로 본다) 클래스 변수도 마찬가지 변수는 별도의 메모리에 저장됨.

  • 메소드 오버라이딩은 메모리상의 객체 타입을 따른다. (실제 객체로 본다)

  • 부모 와 자식 클래스가 같은 메소드를 가지고 있을 경우, 부모 타입으로 자식 객체를 생성하게 되면 메소드를 호출할 때 자식 클래스의 메소드로 호출되는 경우를 가상 메소드 호출이라고 한다. (Virtual Method Call)

    class  Foo{
        static public String y = "super class";
        public String x = "Super";
        public  void methodA(){
            System.out.println("Super method"); 
            // (가상 메소드 호출, virtual method call) 실제로 콜이 이루어지지 않지만 만들어줌 실제로 호출되지않음
            // 없으면 안됨 문법을 맞춰주기 위한 것 뿐 구현된 것 자체는 중요하지 않음..
        }
    }
    
    class Bar extends Foo{
        static public String y = "sub class";
        public String x = "sub";
        @Override
        public void methodA(){
            System.out.println("sub method");
        }
    }
    
    public class Poly02 {
        public static void main(String[] args) {
            Bar bar = new Bar();
            Foo foo = (Foo)bar; // new Bar();
    
            System.out.println(bar.x); //sub
            bar.methodA(); //sub
    
            System.out.println(foo.x); //super
            foo.methodA(); //sub virtural method call
    
            System.out.println(Foo.y);
            System.out.println(Bar.y);
    
    //        System.out.println(foo.y); //super
    //        System.out.println(bar.y); //sub
        }
    }
  • 공변 변환 타입(Covariant return type)

    class Foo{
        public Foo getInstance(){
            return this;
        }
    }
    class Bar extends Foo{
        @Override
        public Bar getInstance(){ 
        //오버라이딩이지만, 리턴 타입이 달라질 수 있다. 여기서는 예외적으로 반환값이 달라진다. 공변 반환 타입
            return this;
            //return (Foo)this; 이거와 같다.
        }
    }
profile
지금, 새로운 문을 열자! 문 저편에 무엇이 있을지 두렵더라도!!

0개의 댓글