객체, 클래스, 인터페이스 유틸리티

객체, 클래스, 인터페이스 유틸리티 (Object, Class, and Interface Utilities)

객체 지향 프로그래밍을 하다 보면 "이 값이 객체인가?", "이 클래스가 저 인터페이스를 구현하는가?" 같은 질문이 필요한 경우가 많아요. object?, class?, interface? 같은 판별자부터, 클래스·인터페이스 정보를 조회하고 봉인(seal)하는 고급 함수까지, 이 절에서 객체 지향 관련 유틸리티들을 한자리에 모아서 살펴볼게요.

출처: Racket Reference

본문

procedure

(object? v) → boolean?
  v : any/c

v가 객체면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (object? (new object%))
#t
> (object? object%)
#f
> (object? "clam chowder")
#f

procedure

(class? v) → boolean?
  v : any/c

v가 클래스면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (class? object%)
#t
> (class? (class object% (super-new)))
#t
> (class? (new object%))
#f
> (class? "corn chowder")
#f

procedure

(interface? v) → boolean?
  v : any/c

v가 인터페이스면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (interface? (interface () empty cons first rest))
#t
> (interface? object%)
#f
> (interface? "gazpacho")
#f

procedure

(generic? v) → boolean?
  v : any/c

v가 generic이면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (define c%
    (class object%
      (super-new)
      (define/public (m x)
        (+ 3.14 x))))
> (generic? (generic c% m))
#t
> (generic? c%)
#f
> (generic? "borscht")
#f

procedure

(object=? a b) → boolean?
  a : object?
  b : object?

ab가 같은 new 호출에서 돌아왔는지 여부를 결정해요. 두 객체에 필드가 있으면, 이 프로시저는 한쪽 필드를 변형하면 다른 쪽의 그 필드도 바뀔지 결정해요.

이 프로시저는 정신적으로는 eq?와 비슷하지만 콘트랙트와도 올바르게 동작해요(그리고 더 강한 보장을 제공해요).

Examples:

> (define obj-1 (new object%))
> (define obj-2 (new object%))
> (define/contract obj-3 (object/c) obj-1)
> (object=? obj-1 obj-1)
#t
> (object=? obj-1 obj-2)
#f
> (object=? obj-1 obj-3)
#t
> (eq? obj-1 obj-1)
#t
> (eq? obj-1 obj-2)
#f
> (eq? obj-1 obj-3)
#f

procedure

(object-or-false=? a b) → boolean?
  a : (or/c object? #f)
  b : (or/c object? #f)

object=?와 같지만 두 인자에 대해 #f를 받아들이고, 두 인자가 모두 #f#t를 돌려줘요.

Examples:

> (object-or-false=? #f (new object%))
#f
> (object-or-false=? (new object%) #f)
#f
> (object-or-false=? #f #f)
#t

base 패키지의 6.1.1.8 버전에서 추가됐어요.

procedure

(object=-hash-code o) → fixnum?
  o : object?

object=? 동등 관계에 해당하는 o의 해시 코드를 돌려줘요.

base 패키지의 7.1.0.6 버전에서 추가됐어요.

procedure

(object->vector object [opaque-v]) → vector?
  object : object?
  opaque-v : any/c = #f

object의 검사 가능한(inspectable) 필드들을 보여주는 벡터를 돌려줘요. struct->vector와 유사해요.

Examples:

> (object->vector (new (class object%
                         (super-new)
                         (field [x 5] [y 10]))))
'#(object:eval:119:0 ...)

procedure

(class->interface class) → interface?
  class : class?

class가 암시적으로 정의하는 인터페이스를 돌려줘요.

Example:

> (class->interface object%)
#<interface:object%>

procedure

(object-interface object) → interface?
  object : object?

object의 클래스가 암시적으로 정의하는 인터페이스를 돌려줘요.

Example:

> (object-interface (new object%))
#<interface:object%>

procedure

(is-a? v type) → boolean?
  v : any/c
  type : (or/c interface? class?)

v가 클래스 type의 인스턴스이거나, 인터페이스 type을 구현하는 클래스의 인스턴스면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (define 2d-point%
    (class* object% (point<%>)
      (super-new)
      (field [x 0] [y 0])
      (define/public (get-x) x)
      (define/public (get-y) y)))
> (is-a? (new 2d-point%) 2d-point%)
#t
> (is-a? (new 2d-point%) point<%>)
#t
> (is-a? (new object%) 2d-point%)
#f
> (is-a? (new object%) point<%>)
#f

procedure

(subclass? v cls) → boolean?
  v : any/c
  cls : class?

vcls에서 파생된(또는 cls와 같은) 클래스면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (subclass? (class object% (super-new)) object%)
#t
> (subclass? object% (class object% (super-new)))
#f
> (subclass? object% object%)
#t

procedure

(implementation? v intf) → boolean?
  v : any/c
  intf : interface?

vintf를 구현하는 클래스면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (define c%
    (class* object% (i<%>)
      (super-new)
      (define/public (go) 'go)))
> (implementation? c% i<%>)
#t
> (implementation? object% i<%>)
#f

procedure

(interface-extension? v intf) → boolean?
  v : any/c
  intf : interface?

vintf를 확장하는 인터페이스면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (define point<%> (interface () get-x get-y))
> (define colored-point<%> (interface (point<%>) color))
> (interface-extension? colored-point<%> point<%>)
#t
> (interface-extension? point<%> colored-point<%>)
#f
> (interface-extension? (interface () get-x get-y get-z) point<%>)
#f

procedure

(method-in-interface? sym intf) → boolean?
  sym : symbol?
  intf : interface?

intf(또는 그 조상 인터페이스 중 하나)의 멤버에 이름이 sym인 것이 있으면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (define i<%> (interface () get-x get-y))
> (method-in-interface? 'get-x i<%>)
#t
> (method-in-interface? 'get-z i<%>)
#f

procedure

(interface->method-names intf) → (listof symbol?)
  intf : interface?

intf의 메서드 이름들을 담은 심볼 리스트를 돌려줘요. 슈퍼인터페이스에서 상속한 메서드를 포함하지만, 이름이 로컬인(즉 define-local-member-name으로 선언된) 메서드는 포함하지 않아요.

Examples:

> (define i<%> (interface () get-x get-y))
> (interface->method-names i<%>)
'(get-x get-y)

procedure

(object-method-arity-includes? object sym cnt) → boolean?
  object : object?
  sym : symbol?
  cnt : exact-nonnegative-integer?

objectcnt개의 인자를 받아들이는 sym이라는 이름의 메서드를 갖고 있으면 #t를, 그렇지 않으면 #f를 돌려줘요.

Examples:

> (define c%
    (class object%
      (super-new)
      (define/public (m x [y 0])
        (+ x y))))
> (object-method-arity-includes? (new c%) 'm 1)
#t
> (object-method-arity-includes? (new c%) 'm 2)
#t
> (object-method-arity-includes? (new c%) 'm 3)
#f
> (object-method-arity-includes? (new c%) 'n 1)
#f

procedure

(field-names object) → (listof symbol?)
  object : object?

object에 바인딩된 모든 필드 이름의 리스트를 돌려줘요. 슈퍼인터페이스에서 상속한 필드를 포함하지만, 이름이 로컬인(즉 define-local-member-name으로 선언된) 필드는 포함하지 않아요.

Examples:

> (field-names (new object%))
'()
> (field-names (new (class object% (super-new) (field [x 0] [y 0]))))
'(x y)

procedure

(object-info object) → (or/c class? #f) boolean?
  object : object?

struct-info의 반환 값과 유사한 두 값을 돌려줘요:

  • class: 클래스 또는 #f예요. 현재 inspector가 그 객체가 인스턴스인 어떤 클래스도 제어하지 않으면 결과는 #f예요.

  • skipped?: 첫 번째 결과가 object의 가장 구체적인 클래스에 해당하면 #f를, 그렇지 않으면 #t를 돌려줘요.

procedure

(class-info class)
  → symbol?
     exact-nonnegative-integer?
     (listof symbol?)
     (any/c exact-nonnegative-integer? . -> . any/c)
     (any/c exact-nonnegative-integer? any/c . -> . any/c)
     (or/c class? #f)
     boolean?
  class : class?

struct-type-info의 반환 값과 유사한 일곱 값을 돌려줘요:

  • name: 클래스의 이름(심볼).

  • field-cnt: 클래스가 정의한 필드(공개·비공개)의 수.

  • field-name-list: 클래스의 공개 필드에 해당하는 심볼 리스트. 이 리스트는 상속된 필드를 포함하므로 field-cnt보다 클 수 있어요.

  • field-accessor: 클래스의 인스턴스에서 필드 값을 얻는 접근자 프로시저. 접근자는 인스턴스와, 0(포함)과 field-cnt(제외) 사이의 필드 인덱스를 받아요.

  • field-mutator: 클래스의 인스턴스에서 필드 값을 수정하는 변경자 프로시저. 변경자는 인스턴스, 0(포함)과 field-cnt(제외) 사이의 필드 인덱스, 새 필드 값을 받아요.

  • super-class: 현재 inspector가 제어하는, 주어진 클래스의 가장 구체적인 조상에 대한 클래스. inspector가 제어하는 조상이 없으면 #f.

  • skipped?: 여섯 번째 결과가 가장 구체적인 조상 클래스면 #f를, 그렇지 않으면 #t를 돌려줘요.

struct

(struct exn:fail:object exn:fail ()
    #:extra-constructor-name make-exn:fail:object)

클래스 관련 실패(예: 객체가 제공하지 않는 메서드를 호출하려는 시도)에 대해 발생해요.

procedure

(class-seal class key unsealed-inits unsealed-fields unsealed-methods
            inst-proc member-proc) → class?
  class : class?
  key : symbol?
  unsealed-inits : (listof symbol?)
  unsealed-fields : (listof symbol?)
  unsealed-methods : (listof symbol?)
  inst-proc : (-> class? any)
  member-proc : (-> class? (listof symbol?) any)

심볼 key로 키가 매겨진 봉인(seal)을 주어진 클래스에 추가해요. 주어진 unsealed-inits, unsealed-fields, unsealed-methods 리스트는 봉인에 영향을 받지 않는 해당 클래스 멤버를 나열해요.

클래스에 봉인이 있으면, 인스턴스화할 때 inst-proc 프로시저가 호출되고(보통 인스턴스화 시 오류를 일으키는 데 사용돼요), 서브클래스가 unsealed 리스트에 없는 클래스 멤버를 추가하려고 하면 member-proc 함수가 호출돼요(역시 보통 오류를 일으키는 데 사용돼요).

inst-proc는 인스턴스화가 시도된 클래스 값과 함께 호출돼요. member-proc는 클래스 값과 초기화 인자·필드·메서드 이름의 리스트와 함께 호출돼요.

procedure

(class-unseal class key wrong-key-proc) → class?
  class : class?
  key : symbol?
  wrong-key-proc : (-> class? any)

class-seal 함수와 주어진 key로 이전에 봉인된 클래스의 봉인을 제거해요.

unseal이 클래스의 모든 봉인을 제거하면, 그 클래스 값은 자유롭게 인스턴스화되거나 서브클래싱될 수 있어요. 주어진 클래스 값에 봉인이 없거나 주어진 key로 된 봉인이 없으면 wrong-key-proc 함수가 클래스 값과 함께 호출돼요.

더 알아보기