DEFCLASS 매크로
DEFCLASS 매크로 (defclass)
defclass는 Common Lisp 객체 시스템(CLOS)에서 이름 붙은 새 클래스를 정의하는 매크로예요. 슬롯(slot) 목록, 상위 클래스, 초기화 인자, 메타클래스 등을 한 폼에 선언해서, 클래스 객체를 만들고 슬롯에 대한 읽기·쓰기 메서드를 자동 생성할 수 있게 해줍니다. 객체 지향 코드에서 가장 먼저 만나는 정의 도구예요.
문법 (Syntax)
defclass class-name ({superclass-name}*) ({slot-specifier}*) [[class-option]]
=> new-class
slot-specifier::= slot-name | (slot-name [[slot-option]])
slot-name::= symbol
slot-option::= {:reader reader-function-name}* |
{:writer writer-function-name}* |
{:accessor reader-function-name}* |
{:allocation allocation-type} |
{:initarg initarg-name}* |
{:initform form} |
{:type type-specifier} |
{:documentation string}
function-name::= {symbol | (setf symbol)}
class-option::= (:default-initargs . initarg-list) |
(:documentation string) |
(:metaclass class-name)
인자와 값 (Arguments and Values)
class-name— non-nil 심볼이에요.superclass-name— non-nil 심볼이에요.slot-name— 변수 이름으로 문법적으로 유효한 심볼이에요.reader-function-name— non-nil 심볼이에요.:reader는 한 슬롯에 여러 번 줄 수 있어요.writer-function-name— 제네릭 함수 이름이에요.:writer는 한 슬롯에 여러 번 줄 수 있어요.reader-function-name(:accessor) — non-nil 심볼이에요.:accessor는 한 슬롯에 여러 번 줄 수 있어요.allocation-type—(member :instance :class)중 하나예요.:allocation은 한 슬롯에 최대 한 번 줄 수 있어요.initarg-name— 심볼이에요.:initarg는 한 슬롯에 여러 번 줄 수 있어요.form— 폼이에요.:init-form은 한 슬롯에 최대 한 번 줄 수 있어요.type-specifier— 타입 지정자예요.:type은 한 슬롯에 최대 한 번 줄 수 있어요.class-option— 클래스 전체 또는 모든 클래스 슬롯을 가리켜요.initarg-list— 초기화 인자 이름과 기본 초기값 폼이 번갈아 나오는 리스트예요.:default-initargs는 최대 한 번 줄 수 있어요.new-class— 새 클래스 객체예요. (:metaclass는 최대 한 번 줄 수 있어요.)
설명 (Description)
defclass 매크로는 이름 붙은 새 클래스를 정의하고, 그 결과로 새 클래스 객체를 돌려줘요.
defclass 문법은 슬롯의 초기화 인자 지정, 슬롯의 기본 초기값 지정, 그리고 지정된 제네릭 함수에 슬롯 값을 읽고 쓰는 메서드를 자동 생성하도록 요청하는 옵션을 제공해요. 기본적으로 읽기·쓰기 함수는 정의되지 않아서 그 생성을 명시적으로 요청해야 해요. 다만 슬롯은 언제나 slot-value로 접근할 수 있어요.
새 클래스를 정의하면 **같은 이름의 타입(type)**도 함께 정의돼요. (typep object class-name) 술어가 참인 경우는 주어진 객체의 클래스가 class-name이 가리키는 클래스 자신이거나 그 서브클래스일 때예요. 클래스 객체는 타입 지정자로 쓸 수 있어서, (typep object class)는 객체의 클래스가 class 자신이거나 그 서브클래스일 때 참이에요.
class-name 인자는 새 클래스의 **고유 이름(proper name)**을 지정해요. 같은 고유 이름의 클래스가 이미 존재하고 그 클래스가 standard-class의 인스턴스이며, 새 클래스 정의의 defclass 폼이 standard-class 클래스를 지정하면, 기존 클래스는 재정의되고 그 인스턴스(와 서브클래스의 인스턴스)는 다음에 접근할 때 새 정의로 갱신돼요. (Section 4.3.6 참고.)
각 superclass-name 인자는 새 클래스의 **직접 상위 클래스(direct superclass)**를 지정해요. 상위 클래스 리스트가 비어 있으면 메타클래스에 따라 기본값이 정해지는데, standard-class의 기본은 standard-object예요.
새 클래스는 각 직접 상위 클래스, 그들의 직접 상위 클래스 등으로부터 슬롯과 메서드를 상속받아요. (Section 4.3.4 참고.)
다음과 같은 **슬롯 옵션(slot option)**을 쓸 수 있어요.
:reader—reader-function-name으로 이름 붙은 제네릭 함수에, 주어진 슬롯의 값을 읽는 unqualified 메서드를 정의하라고 지정해요.:writer—writer-function-name으로 이름 붙은 제네릭 함수에, 슬롯의 값을 쓰는 unqualified 메서드를 정의하라고 지정해요.:accessor—reader-function-name제네릭 함수에 슬롯을 읽는 unqualified 메서드를, 그리고(setf reader-function-name)제네릭 함수에 setf로 슬롯 값을 수정하는 unqualified 메서드를 정의하라고 지정해요.:allocation— 주어진 슬롯의 저장 공간을 어디에 둘지 지정해요. 각 인스턴스에 두거나 클래스 객체 자체에 둘 수 있어요.allocation-type값은:instance또는:class예요. 지정하지 않으면:allocation :instance와 같은 효과예요.:instance면slot-name의 로컬 슬롯이 클래스의 각 인스턴스에 할당돼요.:class면 주어진 이름의 **공유 슬롯(shared slot)**이 이defclass폼이 만든 클래스 객체에 할당돼요. 그 값은 클래스의 모든 인스턴스가 공유해요. 클래스 C1이 그런 공유 슬롯을 정의하면, C1의 어떤 서브클래스 C2도 이 슬롯 하나를 공유해요. 단, C2의defclass폼이 같은 이름의 슬롯을 지정하거나, C2의 클래스 우선순위 리스트에서 C1보다 앞서는 C2의 상위 클래스가 같은 이름의 슬롯을 정의하면 예외예요.
:initform— 슬롯 초기화에 쓸 기본 초기값 폼을 제공해요. 이 폼은 슬롯을 초기화할 때마다 평가돼요. 평가되는 렉시컬 환경은defclass폼이 평가된 렉시컬 환경이에요 (변수와 함수 모두를 가리켜요). 로컬 슬롯의 다이내믹 환경은make-instance가 호출된 다이내믹 환경이고, 공유 슬롯의 다이내믹 환경은defclass폼이 평가된 다이내믹 환경이에요. (Section 7.1 참고.) 어떤 구현도(slot-name form)을(slot-name :initform form)의 축약으로 쓰도록defclass문법을 확장해서는 안 돼요.:initarg—initarg-name이라는 초기화 인자를 선언하고, 이 초기화 인자가 주어진 슬롯을 초기화한다고 지정해요.initialize-instance호출에서 그 초기화 인자에 값이 있으면 그 값이 슬롯에 저장되고, 슬롯의:initform옵션(있으면)은 평가되지 않아요. 주어진 슬롯에 대해 지정된 초기화 인자 중 어느 것에도 값이 없으면,:initform옵션이 지정되어 있을 때 그에 따라 슬롯이 초기화돼요.:type— 슬롯의 내용물이 항상 지정된 데이터 타입임을 지정해요. 이 클래스의 객체에 적용될 때 reader 제네릭 함수의 결과 타입을 사실상 선언해요. 슬롯 타입을 만족하지 않는 값을 슬롯에 저장하려 하면 결과는 정의되지 않아요. (Section 7.5.3에서 더 자세히 다뤄요.):documentation— 슬롯에 문서 문자열을 제공해요. 한 슬롯에 최대 한 번 줄 수 있어요.
다음과 같은 **클래스 옵션(class option)**을 쓸 수 있어요.
:default-initargs— 초기화 인자 이름과 기본 초기값 폼이 번갈아 나오는 리스트가 뒤따라요. 이 초기화 인자들이make-instance에 주어진 초기화 인자 리스트에 없으면, 대응하는 기본 초기값 폼이 평가되고 초기화 인자 이름과 그 폼의 값이 인스턴스가 만들어지기 전에 초기화 인자 리스트 끝에 추가돼요. (Section 7.1 참고.) 기본 초기값 폼은 사용될 때마다 평가돼요. 렉시컬 환경은defclass폼이 평가된 환경, 다이내믹 환경은make-instance가 호출된 환경이에요.:default-initargs클래스 옵션에서 초기화 인자 이름이 두 번 이상 나타나면 오류가 신호돼요.:documentation— 클래스 객체에 문서 문자열을 붙이고,class-name에 kind type으로 문서 문자열을 붙여요. 최대 한 번 줄 수 있어요.:metaclass— 정의 중인 클래스의 인스턴스가 시스템이 기본 제공하는 것(기본은standard-class)과 다른 메타클래스를 갖도록 지정해요.
**표준 클래스(standard class)**에 대한 defclass 규칙은 다음과 같아요.
- 한 클래스의
defclass폼이 평가되기 전에 그 클래스의 상위 클래스들이 정의되어 있을 필요는 없어요. - 한 클래스의 인스턴스를 만들려면 그 전에 그 클래스의 모든 상위 클래스가 정의되어 있어야 해요.
- 클래스는
defmethod폼에서 파라미터 스페셜라이저로 쓰이기 전에 정의되어 있어야 해요.
객체 시스템은 이런 규칙을 따르지 않는 상황을 커버하도록 확장될 수 있어요.
일부 슬롯 옵션은 상위 클래스로부터 상속되고, 일부는 로컬 슬롯 설명을 제공해 가리거나 바꿀 수 있어요. :default-initargs를 제외한 어떤 클래스 옵션도 상속되지 않아요. (Section 7.5.3 참고.)
defclass의 옵션은 확장될 수 있어요. 모든 구현은 로컬로 구현되지 않은 클래스 옵션이나 슬롯 옵션을 발견하면 오류를 신호해야 해요.
한 슬롯에 대해 reader·writer·accessor·초기화 인자를 여러 개 지정하는 건 유효해요. 그 외의 슬롯 옵션은 단일 슬롯 설명에서 두 번 이상 나타날 수 없고, 나타나면 오류가 신호돼요.
슬롯에 reader·writer·accessor가 지정되지 않으면, 그 슬롯은 함수 slot-value로만 접근할 수 있어요.
defclass 폼이 최상위 폼이면, 컴파일러는 클래스 이름이 이후 선언에서 유효한 타입 이름(deftype처럼)으로 인식되고, defmethod 파라미터 스페셜라이저와 이후 defclass의 :metaclass 옵션으로 쓸 유효한 클래스 이름으로 인식되게 해야 해요. 또 컴파일러는 클래스 정의를 find-class가 그 environment 인자로 매크로의 environment 파라미터로 받은 값을 줄 때 반환할 수 있게 해야 해요.
예제 (Examples)
이 페이지에는 예제가 없어요.
영향 (Affected By)
없음.
예외 상황 (Exceptional Situations)
- 중복된 슬롯 이름이 있으면 program-error 타입 오류가 신호돼요.
:default-initargs클래스 옵션에서 초기화 인자 이름이 두 번 이상 나타나면 program-error 타입 오류가 신호돼요.- 단일 슬롯 설명에서
:allocation,:initform,:type,:documentation중 하나가 두 번 이상 나타나면 program-error 타입 오류가 신호돼요. - 모든 구현은 로컬로 구현되지 않은 클래스 옵션이나 슬롯 옵션을 발견하면 program-error 타입 오류를 신호해야 해요.
함께 보기 (See Also)
documentation,initialize-instance,make-instance,slot-value, Section 4.3 (Classes), Section 4.3.4 (Inheritance), Section 4.3.6 (Redefining Classes), Section 4.3.5 (Determining the Class Precedence List), Section 7.1 (Object Creation and Initialization)
참고 (Notes)
없음.