이 문제에 대한 특정 용어가 있는지 모르겠지만 세 가지 일반적인 솔루션 클래스가 있습니다.
- 동적 디스패치에 유리한 콘크리트 유형을 피하십시오
- 형식 제약 조건에 자리 표시 자 형식 매개 변수 허용
- 연관된 타입 / 타입 패밀리를 사용하여 타입 파라미터를 피하십시오
그리고 물론 기본 해결책 : 모든 매개 변수를 계속 철자하십시오.
콘크리트 유형을 피하십시오.
Iterable
인터페이스를 다음과 같이 정의했습니다 .
interface <Element> Iterable<T: Iterator<Element>> {
getIterator(): T
}
이를 통해 인터페이스의 사용자는 정확한 구체적인 유형 T
의 반복자 를 얻을 수 있기 때문에 최대 전력을 제공 합니다. 또한 컴파일러는 인라인과 같은 더 많은 최적화를 적용 할 수 있습니다.
그러나 Iterator<E>
동적으로 전달 된 인터페이스 인 경우 구체적 유형을 알 필요는 없습니다. 예를 들어 Java가 사용하는 솔루션입니다. 인터페이스는 다음과 같이 작성됩니다.
interface Iterable<Element> {
getIterator(): Iterator<Element>
}
이것의 흥미로운 변형은 Rust의 impl Trait
구문 입니다.이 구문은 추상 반환 유형으로 함수를 선언 할 수 있지만 호출 유형에서 구체적인 유형을 알 수 있으므로 최적화가 가능합니다. 이것은 암시 적 유형 매개 변수와 유사하게 작동합니다.
자리 표시 자 유형 매개 변수를 허용합니다.
Iterable
인터페이스는이로 쓸 수있을 때문에, 요소의 형태에 대해 알 필요가 없다 :
interface Iterable<T: Iterator<_>> {
getIterator(): T
}
어디 T: Iterator<_>
제약 조건을 표현하는 "T 관계없이 요소 유형의 반복자입니다." "어떤 종류의 존재로 : 더 엄격하게, 우리는이를 표현할 수있는 Element
그 있도록 T
인 Iterator<Element>
에 대한 구체적인 유형을 알 필요없이," Element
. 이는 type-expression Iterator<_>
이 실제 유형을 설명하지 않으며 유형 제한 조건으로 만 사용할 수 있음을 의미합니다.
유형 패밀리 / 관련 유형을 사용하십시오.
예를 들어 C ++에서 형식에는 형식 멤버가있을 수 있습니다. 이것은 표준 라이브러리 전체에서 일반적으로 사용됩니다 (예 :) std::vector::value_type
. 모든 시나리오에서 유형 매개 변수 문제를 실제로 해결하는 것은 아니지만 유형이 다른 유형을 참조 할 수 있으므로 단일 유형 매개 변수는 전체 유형의 관련 유형을 설명 할 수 있습니다.
정의하자 :
interface Iterator {
type ElementType
fn next(): ElementType
}
interface Iterable {
type IteratorType: Iterator
fn getIterator(): IteratorType
}
그때:
class Vec<Element> implement Iterable {
type IteratorType = VecIterator<Element>
fn getIterator(): IteratorType { ... }
}
class VecIterator<T> implements Iterator {
type ElementType = T
fn next(): ElementType { ... }
}
이것은 매우 유연 해 보이지만 유형 제약을 표현하기가 더 어려울 수 있습니다. 예를 들어 작성된대로 Iterable
반복자 요소 유형을 강요하지 않고 interface Iterator<T>
대신 선언 할 수 있습니다 . 그리고 지금 당신은 상당히 복잡한 유형의 미적분학을 다루고 있습니다. 실수로 그러한 유형의 시스템을 결정할 수 없게 만드는 것은 매우 쉽습니다 (또는 이미 있을까요?).
연관된 유형은 유형 매개 변수의 기본값으로 매우 편리 할 수 있습니다. 예를 들어 Iterable
인터페이스에 요소 유형에 대해 별도의 유형 매개 변수가 필요하지만 일반적으로 반복자 요소 유형과 동일하지는 않지만 자리 표시 자 유형 매개 변수가 있다고 가정하면 다음과 같이 말할 수 있습니다.
interface Iterable<T: Iterator<_>, Element = T::Element> {
...
}
그러나 이것은 언어 인체 공학적 기능 일 뿐이며 언어를 더 강력하게 만들지는 않습니다.
타입 시스템은 어렵 기 때문에 다른 언어에서 작동하는 것과 작동하지 않는 것을 살펴 보는 것이 좋습니다.
예를 들어 Rust Book 의 Advanced Traits 장을 읽고 관련 유형에 대해 설명하십시오. 그러나 제네릭 대신 관련 유형을 선호하는 일부 포인트는 언어에 하위 유형 지정 기능이 없으며 각 특성은 유형 당 최대 한 번만 구현할 수 있기 때문에 적용됩니다. 즉 Rust 특성은 Java와 유사한 인터페이스가 아닙니다.
다른 흥미로운 유형 시스템에는 다양한 언어 확장 기능이있는 Haskell이 있습니다. OCaml 모듈 / 펑 터는 객체 또는 매개 변수화 된 유형과 직접 통합하지 않고 비교적 일반 유형의 유형 패밀리입니다. Java는 유형 시스템에 제한이 있습니다 (예 : 유형이 삭제 된 제네릭, 값 유형에 대한 제네릭 없음). C #은 Java와 매우 유사하지만 구현 복잡성이 증가함에 따라 이러한 제한을 대부분 피할 수 있습니다. Scala는 C # 스타일 제네릭을 Java 플랫폼 위에 Haskell 스타일 타입 클래스와 통합하려고합니다. C ++의 믿을 수 없을 정도로 간단한 템플릿은 잘 연구되었지만 대부분의 제네릭 구현과는 다릅니다.
또한 이러한 언어의 표준 라이브러리 (특히 목록이나 해시 테이블과 같은 표준 라이브러리 컬렉션)를보고 어떤 패턴이 일반적으로 사용되는지 확인할 가치가 있습니다. 예를 들어 C ++에는 다양한 반복 기능이있는 복잡한 시스템이 있으며 Scala는 세밀한 수집 기능을 특성으로 인코딩합니다. Java 표준 라이브러리 인터페이스는 간혹 소리가 나지 Iterator#remove()
않지만 중첩 클래스를 일종의 관련 유형 (예 :)으로 사용할 수 있습니다 Map.Entry
.