목표
- 다형성(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!!");
}
}
}
'프로그래밍 > Unity C#' 카테고리의 다른 글
[Unity] Tutorial 06 - Intermediate Scripting (Extension Methods) (0) | 2020.05.27 |
---|---|
[Unity] Tutorial 06 - Intermediate Scripting (Indexer) (0) | 2020.05.21 |
[Unity] Tutorial 06 - Intermediate Scripting (Encapsulation) (0) | 2020.01.20 |
[Unity] Tutorial 06 - Intermediate Scripting (Information Hiding) (0) | 2020.01.20 |
[Unity] Tutorial 06 - Intermediate Scripting (Method Hiding) (0) | 2020.01.17 |