트랙
/
PHP
PHP
/
학습 과정
/
공변성과 반공변성
공변

공변성과 반공변성 에서 PHP

{other: "%{count}개의 연습 문제"}

공변성과 반공변성 소개

공변성과 반공변성

유니언 타입을 활용할 때는 공변성과 반공변성이라는 개념을 알아 두어야 해요. 이 개념은 문서에 설명되어 있지만, 아래 예제가 도움이 될 거예요.

위 예제와 같은 함수를 사용하는 Calculator 클래스가 있다고 가정해 봐요.

class Calculator
{
    public function add(int|float $x, int|float $y): int|float
    {
        return $x + $y;
    }
}

클래스를 상속받아 확장할 때는 매개변수 타입과 반환 타입을 바꿀 수 있는데, 다음 규칙에 따라서만 가능해요.

먼저, 매개변수 타입은 기반 클래스보다 더 많은 타입을 받아들일 수 있는, 이른바 "더 넓은" 타입이 될 수 있어요. 예를 들어 다음은 허용돼요:

class StringCalc extends Foo
{
    public function add(float|int|string $x, float|int|string $y): int|float
    {
        if (!is_numeric($x) || !is_numeric($y)) {
            throw new \InvalidArgumentException('$x and $y must be numeric');
        }
        return $x + $y;
    }
}

원래의 Calculator 자리에 StringCalc를 대신 넣어도 모든 것이 그대로 동작해요. 호출하는 쪽은 원래 보낼 수 있었던 float나 int를 여전히 보낼 수 있고, 반환 타입도 기대했던 int나 float 그대로예요.

마찬가지로 반환 타입은 좁힐 수 있어요. 유니언에서 타입을 제거해 더 구체적으로 만드는 거예요. 다음 예제는 이 규칙을 활용해요:

class IntCalc extends Calculator
{
    public function add(float|int $x, float|int $y) : int
    {
        return (int)($x + $y);
    }
}

IntCalc는 여전히 int와 float를 받아들이므로 아무 문제 없이 원래의 Calculator를 대신할 수 있어요.

무엇이 허용되고 허용되지 않는지 더 알고 싶으면 리스코프 치환 원칙(LSP)을 참고해요.

허용되지 않는 예는 아래 InvalidCalc를 봐요:

class InvalidCalc extends Calculator
{
    public function add(int $x, int $y) : int|float|string
    {
        // Declaration must be compatible with Calculator->add(x: float|int, y: float|int)

        // Return type declaration must be compatible with Calculator->add(x: float|int, y: float|int) : float|int
    }
}

InvalidCalc 클래스는 컴파일되지 않고 두 가지 문제가 있어요. 매개변수로 허용하는 범위를 줄였기 때문에 InvalidCalc는 float를 더 이상 받아들이지 않으므로 원래의 Calculator를 대신할 수 없어요. 게다가 이제 string을 반환할 수도 있는데, 이 역시 원래 클래스에서 허용하는 반환 타입에 포함되지 않아요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요