클래스 만들기
클래스 만들기
클래스(class)는 Racket 객체 시스템의 핵심이에요. class 형식으로 필드·이니셜라이저·메서드·상속·슈퍼클래스 호출 등을 선언할 수 있어요. 《Racket 가이드》의 [Classes and Objects] 절이 이 개념을 소개해요.
이 절에서는 class, class*, class/derived 형식과 클래스 선언 문법을 다뤄요.
출처: Racket Reference
본문
syntax class
(class superclass-expr class-clause ...)
superclass-expr이 만들어내는 클래스의 서브클래스인 클래스를 만들어요. 클래스는 프로시저와 같은 일급 값이에요. 클래스를 평가하면 새 클래스 객체가 만들어져요.
class-clause들은 클래스의 필드, 메서드, 상속 규칙을 선언해요. 주요 절들은 다음과 같아요.
(init name-expr ...)— 클래스 생성 시 인자로 받는 이니셜라이저들을 선언해요.(init [name-expr default-expr] ...)형태로 기본값을 줄 수도 있어요.(init-field name-expr ...)—init으로 값이 채워지고 동시에 필드가 되는 선언이에요.(field name-expr ...)— 일반 필드를 선언해요.(public name-expr ...)— 공개 메서드를 선언해요.(public (name-expr . formals) body ...+)형태로 메서드 정의를 함께 줄 수 있어요.(private name-expr ...)— 비공개 메서드를 선언해요.(inherit name-expr ...)— 슈퍼클래스의 메서드를 현재 클래스에서 사용할 수 있게 해요.(super name-expr ...)— 슈퍼클래스의 메서드를super로 호출할 수 있게 해요.(override name-expr ...)— 슈퍼클래스의 메서드를 오버라이드해요.(augment name-expr ...)— 슈퍼클래스에서 호출되는 확장이 가능한(augmentable) 메서드를 정의해요.(inner name-expr ...)— 서브클래스가 추가로 구현할 수 있는 "내부(inner)" 메서드를 선언해요.(sequence name-expr ...)— 필드 초기화 순서를 제어해요.(derived id)— 클래스 이름을 지정해요.(method name-expr ...)—class*와 함께 쓰는 방법이에요.
공개 메서드 선언의 예:
(define breakfast%
(class object%
(init size)
(init-field kind)
(field (milk 0))
(define current-size size) ; 로컬 정의 가능
(public (eat amount)
(set! current-size (- current-size amount)))
(super-new)))
super-new는 슈퍼클래스의 이니셜라이저를 호출하는 특별한 형식이에요. (super-new [name-expr val-expr] ...) 형태로 슈퍼클래스 init 인자를 넘길 수 있어요. 모든 클래스 본문은 반드시 super-new를 (직접 또는 간접적으로) 호출해야 해요.
메서드와 필드 접근:
- 메서드 안에서 속한 객체의 메서드를 호출할 때는
(send this method-name arg ...)형태를 쓰거나, public 메서드라면 직접(method-name arg ...)로 호출할 수 있어요. field로 선언한 필드는this를 통해 접근하기보다, 메서드 안에서 직접 이름으로 참조해요.
syntax class*
(class* superclass-expr (interface-expr ...) class-clause ...)
class와 같지만, 두 번째 인자에 인터페이스/속성 표현식들이 추가로 와서 그 클래스가 어떤 인터페이스를 구현하는지 선언해요. interface-expr들은 인터페이스나 구조체 타입 속성 디스크립터이며, 주로 (class* object% (i<%>) ...)처럼 인터페이스를 구현할 때 쓰여요.
예시:
(define i<%> (interface () m))
(define c%
(class* object% (i<%>)
(public (m) 'implements-m)
(super-new)))
syntax class/derived
(class/derived (id . rest-form) superclass-expr class-clause ...)
class와 같지만, (id . rest-form) 하위 형식이 구문 오류 보고를 위한 전체 형식으로 취급되고 그 외에는 무시돼요. 매크로가 class로 확장될 때 사용하도록 의도된 것이에요.
인터페이스와 클래스의 관계:
class로 만든 클래스는 여러 인터페이스를 구현할 수 있어요.- 어떤 클래스가 인터페이스를 구현하는지 확인하려면
implementation?·is-a?등을 써요.
인스턴스 만들기와 메서드 호출:
(define b (new breakfast% [size 2] [kind 'oats]))
(send b eat 1)
new로 클래스의 인스턴스를 만들고, send로 메서드를 호출해요. 클래스와 객체 시스템 전반에 대한 자세한 내용은 《Racket 가이드》의 클래스 관련 절을 참고해요.
syntax send
(send obj-expr method-id arg ...)
(send obj-expr (method-id arg ...))
obj-expr의 결과인 객체에 method-id라는 메시지를 보내요. 즉 객체의 method-id 메서드를 주어진 인자들로 호출하고, 그 메서드의 결과를 돌려줘요. 그 이름을 가진 메서드가 없으면 오류가 발생해요.
procedure
(object? v) → boolean?
v: any/c
v가 객체이면 #t를 돌려줘요.
syntax make-object
(make-object class-expr [init-arg ...])
class-expr의 결과인 클래스의 인스턴스를 만들어요. init-arg들은 클래스의 초기 인자(init 인자)로 전달돼요.
syntax object
(object ([id init-expr] ...) ([id expr] ...) ...)
클래스 정의 없이 바로 객체를 만들어내는 형식이에요. [id init-expr] ...은 객체의 이니셜라이저이고, [id expr] ...은 객체의 메서드를 정의해요.
syntax class->interface
(class->interface id ...)
id라는 이름의 클래스가 구현하는 새 인터페이스를 만들어내요. 그 인터페이스는 해당 클래스가 제대로 제공하는 메서드들을 요구해요. 이렇게 만들어진 인터페이스는 그 클래스가 구현하는 여러 인터페이스의 공통 부분을 추출하는 데 유용해요.