목표

  • 다형성(Polymorphism)에 대해 알아보자.

코바야시네 메이드래곤은, 드래곤이지만 드래곤의 모습과 사람의 모습을 할 수 있다.

다형성(Polymorphism)

  • 다형성이란 하나의 객체가 여러가지 타입을 가질 수 있는 성질을 의미한다.

다형성의 예시

부모 클래스를 사용하여 자식 클래스 생성

  • 아래와 예시에서는 부모 클래스의 참조만 가지고, 자식 클래스를 생성하는 모습이다.
  • 이러한 특징은 하나의 기반 형식에서 파생된 다양한 객체를 표현할 수 있게 한다.
  • 참고로 C#에서 모든 형식은 묵시적으로 object에서 파생되었다.
public class Parent
{
    public void DoSomething()
    {
        Debug.Log("Do Partent !!");
    }
}

public class Child : Parent // Parent 상속
{ 
}

public class Sample : MonoBehaviour
{ 
    void Start()
    {
        Parent p1 = new Parent(); 
        Parent p2 = new Child(); 

        p1.DoSomething(); // Do Partent !!
        p2.DoSomething(); // Do Partent !!
    }
}

virtual & override

public class Child : Parent // Parent 상속
{ 
     public void DoSomething()
    {
        Debug.Log("Do Child !!");
    }
}
  • 그렇다면 위 코드에서 Child 클래스에 DoSomething을 정의하면 어떻게 될까?

  • 답은 부모의 함수 호출결과가 출력된다.

  • 자식에서 부모 클래스에 정의된 함수를 재정의 하려면, 부모 클래스의 메서드가 virtual로 정의되어 있어야 한다.

  • 이때 자식 클래스에서는 override 키워드를 사용하여, 재정의할 메서드를 오버라이드 해야한다.

public class Parent
{
    virtual public void DoSomething()
    {
        Debug.Log("Do Partent !!");
    }
}

public class Child : Parent // Parent 상속
{ 
     public override void DoSomething()
    {
        Debug.Log("Do Partent !!");
    }
}

new

  • 만약 부모 클래스가 가상함수가 아니라면?

  • 메서드 숨기기를 이용하여, 자식 함수에서 부모 클래스의 정의를 숨겨야 한다.

  • 하지만 출력값은 여전히 부모 클래스의 값을 사용한다.

public class Parent
{
    public void DoSomething()
    {
        Debug.Log("Do Partent !!");
    }
}

public class Child : Parent // Parent 상속
{ 
     new public void DoSomething() // 우리 부모님은 이거 없는거다.
    {
        Debug.Log("Do Partent !!");
    }
}

public class Sample : MonoBehaviour
{ 
    void Start()
    {
        Parent p1 = new Parent(); 
        Parent p2 = new Child(); 

        p1.DoSomething(); // Do Partent !!
        p2.DoSomething(); // Do Partent !!
    }
}
  • 이것이 new와 virtual의 차이인데, new의 경우 부모 클래스의 값을 숨긴 것은 Child 클래스이기 때문에
  • Child로 인스턴스 했더라도, 기본형인 Partent의 함수가 호출된다.
public class Sample : MonoBehaviour
{ 
    void Start()
    {
        Parent p = new Parent(); 
        Child c = new Child(); 

        p.DoSomething(); // Do Partent !!
        c.DoSomething(); // Do Child !!
    }
}

abstract

  • abstract를 다시한번 짚고 넘어가보자.
  • 추상 클래스는 인스턴스화 할 수 없다.
  • 추상 클래스에는 추상 메서드 및 접근자가 포함될 수 있다.
  • sealed 한정자는 abstract와 상반되는 키워드이기 때문에, sealed을 사용하여 추상 클래스를 수정할 수 없다.
  • 추상 클래스를 상속받은 비추상 클래스는 반드시 추상 클래스의 모든 상속된 메서드를 실제 구현해야 한다.
  • 추상 메서드의 암시적으로 가상 메서드이다.
  • 추상 메서드 선언은 추상 클래스에서만 가능하다.
  • 추상 메서드 선언은 실제 구현을 제공하지 않는다.
  • 구현은 비추상 클래스의 멤버인 메서드에 override 키워드를 사용하여 구현한다.
  • 추상 메서드 선언에는 static 또는 virtual을 사용하면 오류가 발생한다.
    • 따라서 정적 속성에 abstract를 사용하는 것은 오류이다.
  • 다만 인터페이스는 상속받아 추상 메서드를 매핑할 수 있다.
  • 클래스, 속성, 메서드, 인덱서, 이벤트와 함께 사용될 수 있다.
public interface IHuman
{
    void DoSomething();
}

internal abstract class Parent : IHuman
{
    protected string familyName = "kim";
    public abstract void DoSomething();
}

internal class Child : Parent
{
    public override void DoSomething()
    {
        Debug.Log("Do Child !! : " + familyName);
    }
}

public class Sample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Parent c1 = new Child();
        c1.DoSomething(); // "Do Child !! : kim"

        // or

        IHuman h1 = new Child();
        h1.DoSomething(); // "Do Child !! : kim"
    }
}

sealed

  • sealed를 사용하여, 자식 클래스 까지만 함수가 노출되게 할 수 있다.
public class Parent
{
    public virtual void DoSomething() // 자식 클래스에서 재정의 할 수 있도록 virtual로 정의
    {
        Debug.Log("Do Parent !!");
    }
}
public class Child : Parent  
{  
    sealed public override void DoSomething() // 자식 클래스에 노출되지 않도록 봉인  
    {  
        Debug.Log("Do Child !! : ");  
    }  
}

public class ChildJunior : Child  
{  
    public void DoSomething() // 만약 부모 클래스의 정의를 상속받았다면, 자식 클래스의 함수가 가려질테지만, 봉인했음으로 가려지지 않음 
    {  
        Debug.Log("Do ChildJunior !!");  
    }  
}

public class Sample : MonoBehaviour  
{  
    // Start is called before the first frame update  
    void Start()  
    {  
        ChildJunior jrC = new ChildJunior();  
        jrC.DoSomething(); // 출력해보면 ChildJunior 값이 출력된다.  
    }  
}

Up-Casting, Down-Casting

  • 형변환(castring)은 참보 변수의 타입을 변환하는 것이다.
  • 형변환은 크게 암시적 형변환(자동으로 타입이 변환됨)과 명시적 형변환(강제한 타입을 변환시킴)이 있다.
  • 이때 자식의 타입을 부모 타입으로 변환하는 것을 업케스팅(Up-Casting), 부모의 타입을 자식으로 변환하는 것을 다운캐스팅(Down-Casting)
  • 이때 업케스팅은 암시적으로 변환 가능하지만, 다운 케스팅은 반드시 명시적으로 변화해줘야 한다.
  • 이때, 타입이 변경되는 것은 아님으로, 객체의 데이터가 변경되는 것은 아니다.
public class Parent  
{  
  public virtual void DoSomething()  
  {  
      Debug.Log("Do Parent !!");  
  }  
}

public class Child : Parent  
{  
  public override void DoSomething()  
  {  
      Debug.Log("Do Child !!");  
  }  
}

public class Sample : MonoBehaviour  
{  
  void Start()  
  {  
      // Up-Casting  
      Parent p = null;  
      Child c = new Child();  
      p = c;  
      p.DoSomething(); // Do Child !!

    // Down-Casting
    Parent p1 = new Child();
    Child p2 = null;
    p2 = (Child)p1;
    p1.DoSomething(); // Do Child !!
  }
}
  • 다만, 부모 클래스 참조로 인스턴스를 생성하여, 자식 클래스의 참조로 변환하려는 경우
  • 실제 인스턴스가 가지고 있는 정보와, 대상의 정보가 일치하지 않기 때문에 캐스팅에 실패한다. (InvalidCastException 발생)
  • 아래와 같은 다운 캐스팅의 경우 런타입에서만 오류가 발생한다.
  • 오류를 발생시키지 않고 형변환에 성공했는지 여부를 알아보기 위해 as와 is를 사용한다.
public class Sample : MonoBehaviour  
{  
  // Start is called before the first frame update  
  void Start()  
  {  
    Parent p1 = new Parent();  
    Child p2 = (Child)p1;
    p1.DoSomething();
  }
}

as

  • as는 형변환이 가능하면 지정된 타입으로 인스턴스를 반환하고, 실패하면 null을 반환한다.
public class Sample : MonoBehaviour  
{  
    // Start is called before the first frame update  
    void Start()  
    {  
        Parent p1 = new Child();  
        Child c1 = p1 as Child;  
        Child c2 = p1 as ChildJunior;

        if (c1 != null)
        {
            Debug.Log("is Child!!");
        }

        if (c2 != null)
        {
            Debug.Log("is Child Junior!!");
        }
    }
}

is

  • is는 형변환 가능 여부를 bool 타입으로 반환한다. true인 경우 가능, false경우 불가능이다.
public class Sample : MonoBehaviour  
{  
  // Start is called before the first frame update  
  void Start()  
  {  
      Parent p1 = new Child();

      if (p1 is Child)
      {
          Debug.Log("is Child!!");
      }

      if (p1 is ChildJunior)
      {
          Debug.Log("is Child Junior!!");
      }
    }
}

+ Recent posts