필드·메서드 접근(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의 메서드를 호출하며, 주어진 모든 vkw-arg를 전달합니다.

send*

syntax

(send* obj-expr msg ...+)

msg = (method-id arg ...)
    | (method-id arg ... . arg-list-expr)

같은 객체의 여러 메서드를 (순서대로) 호출합니다. 각 msgsend의 사용에 해당합니다.

예를 들어,

(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이 만들어내는 객체로 시작해 (순서대로) 메서드를 호출합니다. 각 메서드 호출은 마지막 메서드 호출의 결과에 대해 호출되는데, 그 결과는 객체여야 합니다. 각 msgsend의 사용에 해당합니다.

이것은 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 형태와 같지만, 상징적 메서드 이름을 받는 프로시저입니다.

더 알아보기