3.2.1 제약(Constraints)

3.2.1 제약(Constraints)

타입 매개변수는 여러 타입으로 제약될 수 있어요:

typedef Measurable = {
  public var length(default, null):Int;
}

class Main {
  static public function main() {
    trace(test([]));
    trace(test(["bar", "foo"]));
    // String should be Iterable
    // test("foo");
  }

  #if (haxe_ver >= 4)
  static function test<T:Iterable<String> & Measurable>(a:T) {
  #else
  static function test<T:(Iterable<String>, Measurable)>(a:T) {
  #end
    if (a.length == 0)
      return "empty";
    return a.iterator().next();
  }
}

test 메서드는 Iterable<String>Measurable 두 타입으로 제약된 타입 매개변수 T를 가져요. 후자는 편의를 위해 typedef로 정의되며, 호환 타입이 Int 타입의 읽기 전용 속성(property) length를 가지도록 요구합니다. 그러면 제약 조건은 어떤 타입이 다음과 같을 때 호환된다는 것을 나타냅니다.

  • Iterable<String>과 호환되고
  • Int 타입의 length 속성을 가진다.

위 예제에서 7행의 빈 배열과 8행의 Array<String>으로 test를 호출하는 것이 잘 동작하는 것을 볼 수 있어요. Arraylength 속성과 iterator 메서드를 모두 가지기 때문이에요. 하지만 9행처럼 String을 인자로 전달하면 제약 검사를 통과하지 못합니다. StringIterable<T>와 호환되지 않기 때문이에요.

단일 타입으로 제약할 때는 괄호를 생략할 수 있어요:

class Main {
  static public function main() {
    trace(test([]));
    trace(test(["bar", "foo"]));
  }

  static function test<T:Iterable<String>>(a:T) {
    return a.iterator().next();
  }
}

Haxe 4.0.0 이후 — 매크로에서 이 문법의 동작 방식이 약간 달라요. 매크로 컨텍스트에서는 T:Iterable<String> & Measurable 형태가 구조적 제약(structural constraints)과 다르게 취급될 수 있으니 주의해야 합니다. (상세는 원문 참고)

출처: Constraints

본문

제약의 표현

타입 매개변수 제약은 T:제약타입 형태로 : 뒤에 타입을 나열해요. 여러 타입으로 제약할 때는 &(Haxe 4)나 쉼표/괄호(Haxe 4 이전)로 연결합니다.

제약의 의미

제약은 호환 타입이 반드시 만족해야 하는 요건을 정의해요. 예를 들어 Iterable<String> & Measurable 제약은 Iterable<String>과 호환되면서 Int 타입의 length 속성을 가질 것을 요구합니다. StringIterable이 아니므로 이 제약을 통과하지 못해요.

단일 제약

제약이 하나면 괄호를 생략할 수 있어요. T:Iterable<String>처럼 씁니다.

더 알아보기