필드·메서드 접근(Field and Method Access)
필드·메서드 접근(Field and Method Access)
클래스 안에서 필드와 메서드에 접근하는 방법과, 클래스 밖에서 객체의 메서드·필드를 다루는 send·get-field 계열을 살펴볼게요.
출처: Racket Reference
본문
클래스 정의 안의 표현식에서는 클래스의 초기화 변수, 필드, 메서드가 모두 환경의 일부입니다. 메서드 본문 안에서는 클래스의 필드와 다른 메서드만 참조할 수 있습니다. 그 외의 클래스가 도입한 식별자에 대한 참조는 문법 오류입니다. 클래스 안의 다른 곳에서는 클래스가 도입한 모든 식별자를 사용할 수 있고, 필드와 초기화 변수는 set!로 변경할 수 있습니다.
메서드(Methods)
클래스 안에서 사용되는 메서드 이름은 적용 표현식의 프로시저 위치에서만 사용될 수 있습니다. 그 외의 사용은 문법 오류입니다.
메서드를 인자 리스트에 적용할 수 있게 하려면 메서드 적용이 다음 형태를 가질 수 있습니다:
(method-id arg ... . arg-list-expr)
이 형태는 (apply method-id arg ... arg-list-expr)와 유사한 방식으로 메서드를 호출합니다. arg-list-expr는 괄호로 감싼 표현식이어서는 안 됩니다.
메서드는 클래스 밖에서 send, send/apply, send/keyword-apply 형태로 호출됩니다.
send
syntax
(send obj-expr method-id arg ...)
(send obj-expr method-id arg ... . arg-list-expr)
obj-expr을 평가해 객체를 얻고, 객체에 대해 (외부) 이름 method-id를 가진 메서드를 호출하며, arg 결과들을 인자로 제공합니다. 각 arg는 #%app의 경우와 같습니다: arg-expr 또는 keyword arg-expr. 두 번째 형태에서 arg-list-expr는 괄호로 감싼 표현식일 수 없습니다.
obj-expr이 객체를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. 객체에 method-id라는 이름의 공개 메서드가 없으면 exn:fail:object 예외가 발생합니다.
send/apply
syntax
(send/apply obj-expr method-id arg ... arg-list-expr)
send의 점(dotted) 형태와 같지만, arg-list-expr는 어떤 표현식이든 될 수 있습니다.
send/keyword-apply
syntax
(send/keyword-apply obj-expr method-id
keyword-list-expr value-list-expr
arg ... arg-list-expr)
send/apply와 같지만, keyword-apply처럼 키워드와 인자 리스트에 대한 표현식을 가집니다.
dynamic-send
procedure
(dynamic-send obj method-name v ... #:<kw> kw-arg ...) → any
obj : object?
method-name : symbol?
v : any/c
kw-arg : any/c
이름이 method-name과 일치하는 obj의 메서드를 호출하며, 주어진 모든 v와 kw-arg를 전달합니다.
send*
syntax
(send* obj-expr msg ...+)
msg = (method-id arg ...)
| (method-id arg ... . arg-list-expr)
같은 객체의 여러 메서드를 (순서대로) 호출합니다. 각 msg는 send의 사용에 해당합니다.
예를 들어,
(send* edit (begin-edit-sequence)
(insert "Hello")
(insert #\newline)
(end-edit-sequence))
는 다음과 같습니다:
(let ([o edit])
(send o begin-edit-sequence)
(send o insert "Hello")
(send o insert #\newline)
(send o end-edit-sequence))
send+
syntax
(send+ obj-expr msg ...)
msg = (method-id arg ...)
| (method-id arg ... . arg-list-expr)
obj-expr이 만들어내는 객체로 시작해 (순서대로) 메서드를 호출합니다. 각 메서드 호출은 마지막 메서드 호출의 결과에 대해 호출되는데, 그 결과는 객체여야 합니다. 각 msg는 send의 사용에 해당합니다.
이것은 send*의 함수형 대응입니다.
예시:
(define point%
(class object%
(super-new)
(init-field [x 0] [y 0])
(define/public (move-x dx)
(new this% [x (+ x dx)] [y y]))
(define/public (move-y dy)
(new this% [y (+ y dy)] [x x]))
(define/public (get-pair)
(cons x y))))
> (send+ (new point%)
(move-x 5)
(move-y 7)
(move-x 12)
(get-pair))
'(17 . 7)
with-method
syntax
(with-method ([id (obj-expr method-id)] ...)
body ...+)
객체에서 메서드를 추출하고, 각 메서드에 대해 (클래스 안의 선언된 메서드와 같은 방식으로) 직접 적용할 수 있는 지역 이름을 바인딩합니다. 각 obj-expr은 객체를 만들어내야 하고, 그 객체는 해당 method-id가 이름인 공개 메서드를 가져야 합니다. 해당 id는 직접 적용될 수 있도록 바인딩됩니다(Methods 참고).
예시:
(let ([s (new stack%)])
(with-method ([push (s push!)]
[pop (s pop!)])
(push 10)
(push 9)
(pop)))
는 다음과 같습니다:
(let ([s (new stack%)])
(send s push! 10)
(send s push! 9)
(send s pop!))
필드(Fields)
get-field
syntax
(get-field id obj-expr)
obj-expr의 값에서 (외부) 이름 id를 가진 필드를 추출합니다.
obj-expr이 객체를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. 객체에 id 필드가 없으면 exn:fail:object 예외가 발생합니다.
dynamic-get-field
procedure
(dynamic-get-field field-name obj) → any/c
field-name : symbol?
obj : object?
이름이 field-name과 일치하는 obj의 필드를 추출합니다. 객체에 field-name과 일치하는 필드가 없으면 exn:fail:object 예외가 발생합니다.
set-field!
syntax
(set-field! id obj-expr expr)
obj-expr의 값에서 (외부) 이름 id를 가진 필드를 expr의 값으로 설정합니다.
obj-expr이 객체를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. 객체에 id 필드가 없으면 exn:fail:object 예외가 발생합니다.
dynamic-set-field!
procedure
(dynamic-set-field! field-name obj v) → void?
field-name : symbol?
obj : object?
v : any/c
이름이 field-name과 일치하는 obj의 필드를 v로 설정합니다. 객체에 field-name과 일치하는 필드가 없으면 exn:fail:object 예외가 발생합니다.
field-bound?
syntax
(field-bound? id obj-expr)
obj-expr의 객체 결과가 (외부) 이름 id를 가진 필드를 갖고 있으면 #t, 그 외에는 #f를 만듭니다.
obj-expr이 객체를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다.
class-field-accessor
syntax
(class-field-accessor class-expr field-id)
class-expr이 만들어내는 클래스의 인스턴스를 받아 객체의 (외부) 이름 field-id를 가진 필드의 값을 반환하는 접근자 프로시저를 반환합니다.
class-expr이 클래스를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. 클래스에 field-id 필드가 없으면 exn:fail:object 예외가 발생합니다.
class-field-mutator
syntax
(class-field-mutator class-expr field-id)
class-expr이 만들어내는 클래스의 인스턴스와 값을 받아 객체의 (외부) 이름 field-id를 가진 필드의 값을 주어진 값으로 설정하는 수정자 프로시저를 반환합니다. 결과는 #<void>입니다.
class-expr이 클래스를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. 클래스에 field-id 필드가 없으면 exn:fail:object 예외가 발생합니다.
제네릭(Generics)
제네릭(generic)은 메서드 이름을 대신해 사용할 수 있어서, 클래스 안에서 메서드를 이름으로 재배치하는 비용을 피해 메서드 호출을 더 효율적으로 만듭니다.
예시:
(define woody%
(class object%
(define/public (draw who)
(format "reach for the sky, ~a" who))
(super-new)))
(define gen-draw (generic woody% draw))
(define (call-draw o)
(send-generic o gen-draw "partner"))
> (call-draw (new woody%))
"reach for the sky, partner"
(define woody2%
(class woody%
(define/override (draw who)
(string-append (super draw who)
"–there's a snake in my boot!"))
(super-new)))
> (call-draw (new woody2%))
"reach for the sky, partner–there's a snake in my boot!"
generic
syntax
(generic class-or-interface-expr id)
class-or-interface-expr이 만들어내는 클래스나 인터페이스의 인스턴스(또는 class-or-interface에서 파생된 클래스/인터페이스의 인스턴스)에 대해, (외부) 이름 id를 가진 메서드를 호출하는 제네릭을 만듭니다.
class-or-interface-expr이 클래스나 인터페이스를 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. 결과 클래스나 인터페이스에 id라는 이름의 메서드가 없으면 exn:fail:object 예외가 발생합니다.
예시는 Generics 소개를 참고하세요.
send-generic
syntax
(send-generic obj-expr generic-expr arg ...)
(send-generic obj-expr generic-expr arg ... . arg-list-expr)
generic-expr이 만들어내는 제네릭이 나타내는 대로 obj-expr이 만들어내는 객체의 메서드를 호출합니다. 각 arg는 #%app의 경우와 같습니다: arg-expr 또는 keyword arg-expr. 두 번째 형태는 arg-list-expr이 괄호로 감싼 표현식이 아닌 apply로 프로시저를 호출하는 것과 유사합니다.
obj-expr이 객체를 만들어내지 않거나 generic-expr이 제네릭을 만들어내지 않으면 exn:fail:contract 예외가 발생합니다. obj-expr의 결과가 generic-expr의 결과가 캡슐화한 클래스나 인터페이스의 인스턴스가 아니면 exn:fail:object 예외가 발생합니다.
예시는 Generics 소개를 참고하세요.
make-generic
procedure
(make-generic type method-name) → generic?
type : (or/c class? interface?)
method-name : symbol?
generic 형태와 같지만, 상징적 메서드 이름을 받는 프로시저입니다.