프로그래밍 / C++ / 언리얼

Programming/C# | Unity

[Unity] 상속 & 인터페이스 총 정리

아트성 2022. 8. 14. 00:05

상속

현실세계는 어떤 공통적인 특징이 있고, 그 특징을 상속받아 다른 세부적인 항목을 정의하는데, 일상적인 많은 객체가 이러한 "계층적"인 관계를 따르고 있다. 예를들어 고양이, 강아지, 사자라는 타입을 정의한다고 해보자, 공통적으로 그것들은 걷기, 울음소리 등 어떤 상태값을 제공하는데, 이를 정리하면 모두 동물이라는 특징에서 나온 것임을 알 수 있다. 이와같이 공통적인 특징들을 코드상으로 물려주어 정의하는것을 상속이라고 부른다.

 

 

Virtual 메소드를 상속

가상함수는 객체지향 4대특징중 하나인 다형성을 반영시킨 함수이다. 다형성은 문자 그대로 해석하면 여러가지 형태를 띠는것을 의미한다.

 

예를들어 animal이라는 클래스안에 speak()라는 함수를 정의했다고 치자, 동물의 울음소리는 제각각이다. 따라서 상속받은 하위 객체의 울음소리도 재정의 해 줄 필요가있다. 그러기 위해서 virtual이라는 예약어를 부모클래스 단계에서 명시하고 자식클래스에서는 override라는 예약어를 명시해주면 된다. 또한 부모클래스를 명시적으로 가리키는데 사용되는 base

예약어를 사용하여 부모클래스의 멤버를 호출할 수있다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Animal : MonoBehaviour
{
    // 자식클래스에 다시 재정의하고 싶을때는 virtual을 붙여주면된다.
    protected virtual void Speak()
    {
        Debug.Log("????");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cat : Animal
{
    private void Start()
    {
        Info();
    }

    protected override void Speak()
    {
        base.Speak(); // 부모클래스에 있는 함수를 출력
        Debug.Log("야옹~");
    }
}

 

 

 

 

추상클래스의 메소드를 상속

앞에서 설명한 메서드 오버라이드는 일반적으로 virtual 메서드를 정의했다.

그런데 때로는 부모 클래스의 인스턴스를 생성하지 못하게 하면서 특정 메서드에 대해 자식들이 반드시 재정의하도록 강제하고 싶을 수 있다. 이 같은 상황을 위해서 abstract class와 abstract method가 필요한 것이다. 추상클래스를 사용하려면 부모클래스와, 그의 필드에 abstract 예약어를 추가해주면 된다.

 

아래는 Electronics클래스와 그의 필드들을 추상화시켜보았다. 그리고 아래는 추상클래스를 사용할 때 주의사항 2가지를 나열해 보았다.

 

 

1. 추상클래스는 인스턴스를 생성할 수 없다.

 

2. 추상클래스의 메서드는 자식클래스에서 반드시 정의해야한다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

abstract public class Electronics : MonoBehaviour
{
    // 자식클래스에서 무조건 완성을 시켜야 하는 함수.
    abstract protected void turnOn();
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Camera : Electronics
{
    string cameraName;

    private void Start()
    {
        turnOn();
    }

    protected override void turnOn()
    {
        Debug.Log("카메라 전원이 켜졌습니다.");
    }
}

 

인터페이스

추상클래스로 구현할 수 있는것을 왜 굳이 interface라는 새로운 예약어를 만들어 표현하게 됐을까?

그에 대한 유일한 이유는 클래스는 다중 상속이 불가능하기 때문이다. 따라서 다중상속을 받기 위해서는 interface를 정의해야한다.

 

다음은 추상클래스로 상속받았을 때이다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

abstract public class Electronics : MonoBehaviour
{
    abstract public void reducedBatteryCapacity();
}

abstract public class Display : MonoBehaviour
{
    abstract public void ScreenOn();
}

// Error! : 클래스는 다중상속이 불가능 하다.
public class Camera : Electronics, Display
{
    public override void reducedBatteryCapacity()
    {
        Debug.Log("배터리가 1만큼 감소.");
    }

    public override void ScreenOn()
    {
        Debug.Log("화면이 켜졌습니다.");
    }
}

 

위와같이 추상클래스를 여러개 상속받는 코드를 작성하면, 컴파일 오류가 뜨게된다.

그러나 인터페이스 같은 경우는 다중상속이 가능하다. 왜냐하면 인터페이스는 클래스가 아니기 때문이다.

(주의할 점은 자식클래스에서 구현할 때는 반드시 public 접근제한자를 명시해야 한다는 것이다.)

 

아래는 인터페이스를 구현한 코드이다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

interface Electronics
{
    void reducedBatteryCapacity();
}

interface Display
{
    void ScreenOn();
}

// Error! : 클래스는 다중상속이 불가능 하다.
public class Camera : MonoBehaviour, Electronics, Display
{
    public void reducedBatteryCapacity()
    {
        Debug.Log("배터리가 1만큼 감소.");
    }

    public void ScreenOn()
    {
        Debug.Log("화면이 켜졌습니다.");
    }

    void Start()
    {
        reducedBatteryCapacity();
        ScreenOn();
    }
}

위와같이 Camera클래스는 MonoBehaviour외에 Electronics, Display 2가지 인터페이스를 상속받고 있으며, 다중상속이 허용되어 여러모로 유용하게 쓸 수 있다. 아래와 같이 유니티에서도 정상적으로 작동되는것을 확인할 수 있다.

 

인터페이스는 메서드 뿐만아니라 프로퍼티를 포함하는것이 가능하다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

interface Display
{
    void ScreenOn();
    int Width { get; set; } // 프로퍼티 get/set 포함
    int Height { get; } // get만 포함하는 것도 가능
}

// Error! : 클래스는 다중상속이 불가능 하다.
public class Camera : Display
{
    int width;
    int height;
    public int Width
    {
        get{ return width; }

        set { width = value; }
    }

    public int Height { get { return height; } }

    public void ScreenOn()
    {
        Debug.Log("화면이 켜졌습니다.");
    }

}

 

반응형