Menu

PHP 상속: extends, parent::, 오버라이딩

PHP에서 클래스는 class Dog extends Animal로 다른 클래스를 상속합니다. 부모의 public과 protected 속성과 메서드를 받고, 그것을 오버라이드할 수 있으며, parent::method()로 원래 것도 호출할 수 있습니다. 오버라이딩, protected, final, instanceof, private 메서드 함정을 알아봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

PHP에서 클래스는 extends로 다른 클래스를 상속합니다. class Dog extends Animal은 Dog에 Animal의 모든 public과 protected 속성과 메서드를 줍니다. 자식은 새 메서드를 추가하고, 같은 이름의 메서드를 선언해 상속받은 메서드를 오버라이드하며, parent::method()로 원래 것을 호출할 수 있습니다.

Dog에는 생성자가 없으므로 Animal의 것을 쓰며, 그래서 new Dog('Rex')가 동작합니다. 자식 클래스는 부모를 하나만 상속할 수 있습니다. PHP에는 클래스의 다중 상속이 없습니다.

parent::로 부모 메서드 호출하기

오버라이딩은 부모의 메서드를 완전히 대체합니다. 대체하는 대신 확장하려면 자식에서 parent::method()를 호출하고 그 결과 위에 쌓으세요.

생성자도 parent::__construct(...)로 똑같이 동작하며, 빠뜨렸을 때 일어나는 일을 포함해 생성자 페이지에서 다룹니다.

자식 클래스에서 protected와 private

protected 멤버는 바깥 코드에는 숨겨지지만 가족의 모든 클래스에는 보입니다. private 멤버는 선언한 클래스에서만 보이므로 자식 클래스는 전혀 볼 수 없습니다.

자식은 부모의 private $pin에 접근할 수 없으므로 자식에서 isset($this->pin)은 거짓이고, 부모 자신의 메서드 hasPin()은 여전히 그것을 읽습니다. 하위 클래스가 기반으로 삼아야 할 것에는 protected를, 자유롭게 바꿀 수 있게 남겨 두고 싶은 세부 사항에는 private을 쓰세요.

다형성과 instanceof

부모 타입에 맞춰 쓴 코드는 모든 자식과 동작합니다. 각 객체가 오버라이드한 메서드의 자기 버전을 실행하므로 반복문 하나로 모두 처리할 수 있습니다. instanceof는 객체가 어떤 클래스나 그 자식의 것인지 확인합니다.

부모를 단독으로 쓰면 안 된다면(넓이가 0인 그냥 Shape는 별 의미가 없습니다) 추상 클래스로 만들고 area()를 abstract로 선언하세요.

오버라이딩 규칙: 시그니처는 호환되어야 한다

오버라이드하는 메서드는 부모가 받던 것을 최소한 받아야 하고, 부모를 호출하던 쪽이 쓸 수 있는 것을 반환해야 합니다. 선택적 매개변수를 추가하거나, 매개변수 타입을 넓히거나, 반환 타입을 좁힐 수는 있습니다. 필수 매개변수를 추가하거나 public 메서드를 private으로 만들 수는 없습니다.

string $sep = ','를 필수인 string $sep로 바꾸면 PHP는 치명적 에러("Declaration of CsvExporter::export(array rows,stringrows, string sep): string must be compatible with Exporter::export(array $rows): string")와 함께 클래스 컴파일을 거부합니다.

final 클래스와 메서드

final은 그 지점에서 상속을 멈춥니다. final 메서드는 오버라이드할 수 없고, final class는 상속할 수 없습니다. 둘 다 파일을 컴파일할 때 검사되므로 스크립트가 아예 시작되지 않습니다.

<?php
class Payment
{
    final public function charge(int $cents): string
    {
        return "charged $cents";
    }
}

class FreePayment extends Payment
{
    public function charge(int $cents): string
    {
        return 'free';
    }
}
PHP Fatal error:  Cannot override final method Payment::charge() in /home/index.php on line 12

final 클래스라면 메시지는 Class B cannot extend final class A입니다. 무엇보다 먼저 권한을 확인하는 메서드처럼, 부모가 메서드가 정확히 한 가지 방식으로 동작한다고 기대할 때 final로 표시하세요.

private 메서드 함정

private 메서드는 상속되지 않으므로 같은 이름의 자식 메서드가 그것을 오버라이드하지 않습니다. 부모의 코드는 계속 자신의 private 버전을 호출하며, 자식의 메서드가 실행되리라 기대한 사람들은 놀랍니다.

greet()은 여전히 Hello를 출력합니다. 부모의 $this->word()는 부모의 private 메서드로 연결됩니다. Greeter의 private을 protected로 바꾸고 다시 실행해 보세요. 이제 자식의 메서드가 진짜 오버라이드가 되고 greet()이 Bonjour라고 말합니다.

자주 묻는 질문

PHP에서 extends는 어떻게 동작하나요?

class Dog extends Animal은 Dog를 Animal의 자식으로 만듭니다. Dog 객체는 Animal의 모든 public과 protected 속성과 메서드를 가지고, 자신의 것을 추가할 수 있으며, 같은 이름의 메서드를 선언해 상속받은 메서드를 대체(오버라이드)할 수 있습니다.

PHP에서 부모 메서드를 호출하려면 어떻게 하나요?

자식의 메서드 안에서 parent::methodName()을 쓰세요. 예: return parent::describe() . ' and more';. 생성자라면 parent::__construct(...)입니다.

PHP는 다중 상속을 지원하나요?

아니요. 클래스는 부모 클래스를 하나만 상속할 수 있습니다. 여러 곳의 동작을 공유하려면 여러 인터페이스를 구현하고(class A implements B, C) 트레이트를 쓰세요(use Loggable, Timestamps;).

PHP에서 final은 무엇을 뜻하나요?

final 메서드는 자식 클래스가 오버라이드할 수 없고, final class는 아예 상속할 수 없습니다. 어느 규칙이든 어기면 클래스를 컴파일할 때 치명적 에러가 납니다. 예: Cannot override final method A::f().

PHP에서 자식 클래스가 부모의 private 속성에 접근할 수 있나요?

아니요. private 멤버는 선언한 클래스에만 속하므로 자식에서 $this->pin은 정의되지 않은 속성(경고 후 null)이며, 같은 이름의 자식 메서드는 private 메서드를 오버라이드하지 않습니다. 자식 클래스에 필요하다면 멤버를 protected로 선언하세요.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기