클로저 – 05.익명 클래스

클래스를 문서화 할 필요가 없이 일회성으로 실행되는 경우에 사용할 수 있도록 PHP 7.0부터 이름 없는 익명 클래스(anonymous class)를 지원합니다.

익명 클래스는 아래와 같은 구문으로 작성됩니다.

크고 복잡한 구조의 프로그램이거나 코드 재사용이 필요한 경우에는 일반적인 기명 클래스(named class, explicit class)가 유리할 수 있습니다. 그러나 간단한 종속성을 가진 가벼운 인터페이스를 구현하는 경우에는 익명 클래스가 정의한 곳에서 객체를 생성할 수 있어 기명 클래스와 비교해서 유리한 점도 있습니다.

익명 클래스의 객체 기능

익명 클래스는 이름만 없을 뿐 생성자에 인수를 전달하고, 다른 클래스를 상속하고, 인터페이스를 구현하고, 트레이트를 조합하는 등 기명 클래스와 동일한 객체의 기능을 가지고 있습니다.

위의 예는 다음과 같이 출력됩니다.

익명 클래스의 상속은 정상적으로 동작합니다.

트레이트는 기명 클래스 정의에서와 동일하게 동작합니다.

외부 클래스 멤버를 접근하는 방법

객체 내부에 정의된 클로저는 외부 클래스의 $this를 자동적으로 바인딩 해주기 때문에 해당 클래스의 private 및 protected 멤버를 포함한 현재 객체에 대한 전체 접근 권한을 갖습니다.

그러나 익명 클래스는 외부 클래스 내에 중첩하더라도 외부 클래스와는 완전히 다른 별개의 객체이므로 외부 클래스의 $this와 익명 클래스의 $this는 다른 객체를 의미합니다. 따라서 익명 클래스 내에서는 $this를 통해 외부 클래스의 멤버에 접근할 수 없습니다.

public 멤버 접근

익명 클래스에서 외부 클래스의 public 멤버(메소드, 프로퍼티)에 접근하려면 익명 클래스 내에서 외부 클래스의 객체를 생성하거나 외부 클래스의 객체 또는 해당 멤버를 익명클래스에 전달하여야 합니다.

외부 클래스의 객체를 익명클래스에 전달할 때는 생성자를 통해 전달할 수 있습니다.

익명 클래스에서 생성하거나 전달 받은 외부 클래스의 객체는 외부 클래스의 스코프에서 벗어나 있기 때문에 public 멤버만 접근할 수 있습니다.

외부 클래스의 public 멤버에 접근할 수 있는 또 다른 방법은 외부 클래스를 상속하는 방법으로 상속하게 되면 외부 클래스가 부모 클래스가 되기 때문에 public 멤버와 protected 멤버에 접근할 수 있습니다.

protected 멤버 접근

익명 클래스에서 외부 클래스의 protected 멤버(메소드, 프로퍼티)를 사용하려면 외부 클래스를 상속하여야 합니다.

private 프로퍼티 접근

익명 클래스에서 외부 클래스의 private 프로퍼티를 사용하려면 해당 프로퍼티를 생성자를 통해 전달하여야 합니다.

익명 클래스 카운터

외부 클래스에 정의된 private 프로퍼티로 카운터를 작성하기 위해서는 해당 프로퍼티를 익명 클래스 생성자로 전달하여 수행할 수 있습니다.

외부 클래스의 $oCounter 프로퍼티는 익명 클래스로 복사하여 처리하였기 때문에 익명 클래스에서 해당 프로퍼티의 값을 변경하더라도 외부 클래스의 프로퍼티의 값은 변경되지 않습니다.

참조에 의한 연동

해당 프로퍼티를 참조(&)로 익명 클래스에 전달하면 외부 클래스의 프로퍼티와 연동시킬 수 있습니다.

클로저에 의한 연동

또 다른 연동 방법으로 클로저를 이용할 수 있습니다. 클로저를 익명 클래스에서 생성한 후 Closure 클래스의 bindTo 메소드를 사용하여 클로저를 외부 클래스 객체에 바인딩하고 스코프를 변경하게 되면 익명 클래스에서 클로저를 통해 외부 클래스의 모든 멤버에 자유롭게 접근할 수 있습니다.

익명 클래스와 클로저의 유사성

익명 클래스와 클로저 상호 간에 직접적인 관계는 없고 외부 변수를 내부에 정의된 익명 클래스나 클로저로 전달하는 방법은 다릅니다만 유사한 점도 많습니다.

우선은 익명 클래스와 클로저 모두 이름을 지정하지 않는다는 점에서 동일합니다.

그리고 익명 클래스와 클로저 모두 자신의 스코프의 범위 밖에 있는 외부 변수에 접근할 수 있다는 점에서 유사합니다.

특히 외부 변수가 함수의 지역 변수이거나 외부 클래스의 private, protected 프로퍼티인 경우를 보면 일반적인 방법으로는 해당 변수에 접근할 수 없으나 익명 클래스와 클로저를 사용하면 접근할 수 있습니다.

익명 클래스와 클로저 모두 외부 함수가 종료되어 먼저 소멸되더라도 내부로 등록되거나 전달된 외부 함수의 지역 변수는 소멸되지 않으며, 외부 함수 밖에서 내부에 있는 익명 클래스나 클로저를 호출하더라도 외부 함수의 지역 변수에 접근 할 수 있습니다. 즉 자신이 생성될 때의 환경을 기억한다는 점에서도 유사합니다.

클로저와의 유사성 만을 놓고 본다면, 익명 클래스는 클로저의 개념과 상당히 일치하고 있습니다. 스크립트 상 적용된 대상이 클래스냐 함수냐의 차이를 보이기는 합니다만, 단편적인 생각으로는 클로저의 표현을 클래스까지 확장한 것이 아닌가하는 생각이 들기도 합니다.

물론 Java와 같은 다른 객체지향언어에서도 익명 클래스와 클로저를 서로 다른 별개의 개념으로 구분 짓고 있고, PHP에서도 클로저가 Closure 클래스를 통해서 생성되며 Closure 클래스에는 객체 바인딩 관련 메소드들이 정의되어 구현되고 있기 때문에 익명 클래스와는 완전히 다르기는 합니다만 일반적인 클로저의 개념으로 보면 아리송합니다.

익명 클래스의 내부 이름

동일한 익명 클래스 선언에 의해 생성된 모든 객체는 해당 클래스의 인스턴스입니다. 즉 동일한 위치에서 생성된 익명 클래스는 동일한 내부 이름이 할당됩니다.

익명 클래스의 내부 이름은 해당 주소를 기반으로 하는 고유한 이름으로 get_class 함수로 그 이름을 확인할 수는 있으며 class@anonymous 형식으로 구성됩니다.

익명 클래스의 직렬화

익명 클래스의 직렬화(serialization)는 지원되지 않으며 클로저와 마찬가지로 치명적인 오류가 발생합니다.

  • Fatal error: Uncaught Exception: Serialization of ‘class@anonymous’ is not allowed

중첩 클래스

중첩 클래스(nested class)는 클래스 안에 다른 클래스가 정의되는 것으로 외부에 존재하는 클래스를 외부 클래스(outer class)라고 하며 내부에 포함된 클래스를 내부 클래스(inner class)라고 합니다.

자바와 같은 다른 언어에서는 외부 클래스의 멤버처럼 정의되는 내부 클래스가 있습니다. 이러한 내부 클래스를 외부 클래스의 멤버처럼 클래스를 정의한다고 해서 멤버 클래스라고도 하는데, PHP에서는 2013년에 PHP RFC: Nested Classes 문서를 통해 이러한 중첩 클래스(멤버 클래스)를 제안하였지만 PHP에서 받아들이지는 않았습니다.

사실 익명 클래스도 내부 클래스의 일종이기 때문에 향후 PHP 버전에서 중첩 클래스의 구현도 가능할 것으로 보입니다.

답글 남기기