인터페이스 만들기
인터페이스 만들기
인터페이스는 클래스가 구현해야 할 메서드들의 계약을 정의하는 Racket 객체 시스템의 개념이에요. 클래스·객체·인터페이스의 개요는 《Racket 가이드》의 [Classes and Objects] 절에서 소개하고 있어요.
이 절에서는 인터페이스를 만드는 두 형식, interface와 interface*를 다뤄요.
출처: Racket Reference
본문
syntax interface
(interface (super-interface-expr ...) name-clause ...)
`name-clause=`id``|`(id contract-expr)|`(id #:public default-expr)``|`(id #:override default-expr)|`(id contract-expr #:public impl-expr)``|`(id contract-expr #:override impl-expr)
인터페이스를 만든다. id들은 서로 구별되어야 해요.
각 super-interface-expr은 인터페이스 표현식이 평가될 때 (순서대로) 평가돼요. 각 super-interface-expr의 결과는 인터페이스 값이어야 하며, 그렇지 않으면 exn:fail:object 예외가 발생해요. super-interface-expr들이 돌려준 인터페이스들은 새 인터페이스의 슈퍼인터페이스이며, 새 인터페이스는 이들을 모두 확장해요. 새 인터페이스를 구현하는 어떤 클래스든 모든 슈퍼인터페이스도 구현해요.
인터페이스 표현식의 결과는 명시된 모든 id와 슈퍼인터페이스들의 모든 식별자를 포함하는 인터페이스예요. 어떤 id는 대응하는 contract-expr과 짝지어질 수 있고, impl-expr을 가질 수도 있는데, 이는 구현하는 클래스에서 상속되거나 오버라이드될 id의 구현을 제공해요. 각 impl-expr은 메서드 프로시저여야 해요. 「Method Definitions」를 참고해요. 슈퍼인터페이스들 사이의 중복 식별자 이름은, 서로 다른 인터페이스에서 비롯된 각 식별자에 대해 둘 이상이 기본 구현을 제공하지 않는 한 무시돼요.
인터페이스는 어떤 슈퍼인터페이스도 그 메서드의 구현을 갖고 있지 않으면 #:public으로, 그 외에는 #:override로 메서드 구현을 제공할 수 있어요. 여러 슈퍼인터페이스가 서로 다른 조상 인터페이스에서 비롯된 메서드 구현을 제공한다면, 그 메서드는 반드시 오버라이드되어야 해요. super 형식은 인터페이스 메서드 구현 안에서는 지원되지 않아요.
super-interface-expr이 주어지지 않으면, 결과 인터페이스의 파생 요구사항(derivation requirement)은 자명해요. 즉 그 인터페이스를 구현하는 어떤 클래스든 object%에서 파생되어야 해요. 그 외에는 결과 인터페이스의 구현 요구사항이 슈퍼인터페이스들 중 가장 구체적인 요구사항이 돼요. 슈퍼인터페이스들이 일치하지 않는 파생 요구사항을 명시하면 exn:fail:object 예외가 발생해요.
예시:
(define file-interface<%>
(interface ()
open close read-byte write-byte
[append-line
#:public
(λ (bts)
(send this open 'append)
(for ([b (in-bytes bts)])
(send this write-byte b))
(send this close))]))
(define directory-interface<%>
(interface (file-interface<%>)
[file-list (->m (listof (is-a?/c file-interface<%>)))]
parent-directory))
package base 버전 8.17.0.4에서 변경됨: #:public과 #:override 메서드 구현을 지원하기 시작했어요.
syntax interface*
(interface* (super-interface-expr ...)
([property-expr val-expr] ...)
name-clause ...)
`name-clause=`id``|`(id contract-expr)|`(id #:public default-expr)``|`(id #:override default-expr)|`(id contract-expr #:public default-expr)``|`(id contract-expr #:override default-expr)
interface와 같지만, property-expr들이 만들어내는 구조체 타입 속성들을 대응하는 val-expr들과 함께 그 인터페이스에 추가로 연관시켜요.
결과 인터페이스(또는 그것에서 파생된 하위 인터페이스)가 class* 형식을 통해 클래스에 명시적으로 구현될 때마다, 각 속성이 그 값과 함께 클래스의 인스턴스로 인스턴스화되는 구조체 타입에 붙어요. 구체적으로 그 속성은 즉시 필드가 0개인 구조체 타입에 붙는데, 이 구조체 타입은 확장되어 클래스 인스턴스의 내부 구조체 타입을 만들어내요(그래서 필드에 대한 어떤 정보도 구조체 타입 속성의 guard(있으면)가 접근할 수 없어요).
예시:
(define i<%> (interface* ()
([prop:custom-write
(lambda (obj port mode) (void))])
method1 method2 method3))
package base 버전 8.17.0.4에서 변경됨: #:public과 #:override 메서드 구현을 지원하기 시작했어요.