공분산 및 역분산(컴퓨터 과학)

Covariance and contravariance (computer science)

많은 프로그래밍 언어 유형 시스템이 하위 유형을 지원합니다.예를 들어 다음과 같이 입력합니다.Cat의 서브타입입니다.Animal, 그 다음 타입의 식입니다.Cat 활자의 표현은 어디에서나 대체할 수 있어야 한다.Animal사용됩니다.

분산은 보다 복잡한 유형 간의 서브타이핑이 구성요소 간의 서브타이핑과 어떻게 관련되어 있는지를 나타냅니다.예를 들어, 리스트는 어떻게 하면 좋을까요?Cat리스트와 관련되다Animals? 또는 함수를 반환하는 방법Cat반환되는 함수와 관련되다Animal?

유형 생성자의 분산에 따라 단순 유형의 하위 유형 관계는 각 복합 유형에 대해 유지되거나 반전되거나 무시될 수 있습니다.예를 들어 OCaml 프로그래밍 언어에서 "list of Cat"은 목록 유형 생성자가 공변이기 때문에 "list of Animal"의 하위 유형입니다.즉, 단순 유형의 하위 유형 관계가 복합 유형에 대해 보존됩니다.

한편, "동물에서 문자열로 함수"는 "Cat에서 문자열로 함수"의 하위 유형으로, 함수 유형 생성자가 매개 변수 유형에 반하기 때문입니다.여기서 복소형에서는 단순형의 서브타이핑 관계가 반전된다.

프로그래밍 언어 설계자는 배열, 상속 및 일반 데이터 유형과 같은 언어 기능에 대한 입력 규칙을 고안할 때 차이를 고려합니다.유형 생성자를 불변량 대신 공변량 또는 반변량으로 만들면 더 많은 프로그램이 잘 입력된 것으로 받아들여질 것입니다.한편, 프로그래머들은 종종 반칙이 비합리적이라는 것을 알게 되고, 런타임 타입 오류를 피하기 위해 분산을 정확하게 추적하면 복잡한 타이핑 규칙이 생길 수 있습니다.

유형 시스템을 단순하게 유지하고 유용한 프로그램을 허용하기 위해, 언어는 유형 생성자를 변형으로 간주하는 것이 안전할지라도 유형 생성자를 불변으로 취급하거나, 유형 안전성을 위반할 수 있더라도 공변으로 취급할 수 있다.

형식적 정의

프로그래밍 언어의 유형 시스템 내에서 타이핑 규칙 또는 유형 생성자는 다음과 같습니다.

  • 공변량 - 유형(유형)순서를 보존하는 경우, 보다 특정적인 유형에서 보다 일반적인 유형으로 정렬합니다.한다면A ≤ B,그리고나서I<A> ≤ I<B>;
  • 순서를 반대로 했을 경우, 다음과 같이 됩니다.한다면A ≤ B,그리고나서I<B> ≤ I<A>;
  • 두 가지가 모두 적용되는 경우(즉, 이 두 가지가 모두 적용되는 경우)A ≤ B,그리고나서I<A> ≡ I<B>);[1]
  • 공변량, 반변량 또는 이변량인 경우 변형
  • 변형이 아닌 경우 불변 또는 비변수.

이 기사에서는 이것이 일부 일반적인 유형의 컨스트럭터에 어떻게 적용되는지에 대해 설명합니다.

C#의 예

를 들어 C#에서는Cat의 서브타입입니다.Animal그 다음에, 다음과 같이 합니다.

  • IEnumerable<Cat>의 서브타입입니다.IEnumerable<Animal>서브타이핑은 다음 이유로 유지됩니다.IEnumerable<T>공변한다.T.
  • Action<Animal>의 서브타입입니다.Action<Cat>서브타이핑이 반전되는 이유는Action<T> 반하는T.
  • 둘 다 아니다.IList<Cat>도 아니다IList<Animal>다른 타입의 서브타입입니다.IList<T>불변하다T.

C# 범용 인터페이스의 바리안스는, 다음과 같이 배치하는 것에 의해서 선언됩니다.out(중요) 또는in(반변수) Atribute on (0 또는 그 이상의) 유형 파라미터.컴파일러는 각 마킹된 유형 파라미터에 대해 위반이 치명적일 경우 그러한 사용이 글로벌하게 일치하는지 최종 확인합니다.위의 인터페이스는 다음과 같이 선언됩니다.IEnumerable<out T>,Action<in T>,그리고.IList<T>. 타입 파라미터가 여러 개 있는 타입은 각 타입 파라미터에 다른 분산을 지정할 수 있습니다.예를 들어 위임 유형Func<in T, out TResult>유형의 역변수 입력 매개 변수를 가진 함수를 나타냅니다.T및 유형의 공변 반환 값TResult를 클릭합니다.[2]

인터페이스 분산에 대한 입력 규칙은 유형의 안전을 보장합니다.예를 들어,Action<T>유형의 인수를 필요로 하는 1등급 함수를 나타냅니다.T고양이만 다룰 수 있는 기능 대신 어떤 종류의 동물도 다룰 수 있는 기능을 항상 사용할 수 있습니다.

어레이

읽기 전용 데이터 유형(소스)은 공변량일 수 있으며 쓰기 전용 데이터 유형(싱크)은 반변량일 수 있습니다.소스와 싱크 역할을 모두 하는 가변 데이터 유형은 불변해야 합니다.이 일반적인 현상을 설명하기 위해 어레이 유형을 고려합니다.타입의 경우Animal우리는 활자를 만들 수 있다.Animal[]이것은 "동물들의 배열"이다.이 예에서는 이 배열이 읽기 및 쓰기 요소를 모두 지원합니다.

다음 중 하나로 취급할 수 있습니다.

  • 공변량: aCat[]는 입니다.Animal[];
  • 반변: anAnimal[]는 입니다.Cat[];
  • 불변: anAnimal[]이 아니다Cat[]및 aCat[]가 아니다Animal[].

유형 오류를 방지하려면 세 번째 선택만 안전합니다.확실히, 모든 것은 아니다.Animal[]마치 …인 것처럼 취급할 수 있다Cat[]어레이에서 읽어낸 클라이언트는,Cat단,Animal[]예를 들어 a를 포함할 수 있습니다.Dog그래서 그 반칙은 안전하지 않다.

반대로, a는Cat[]로서 취급할 수 없다Animal[]. 항상 put을 넣을 수 있어야 합니다.Dog으로Animal[]. 공변 배열에서는 백킹스토어가 실제로 고양이 배열일 수 있기 때문에 안전하다고 보장할 수 없습니다.따라서 공변량 규칙도 안전하지 않습니다. 배열 생성자는 불변해야 합니다.이것은 가변 어레이의 문제일 뿐입니다.공변량 규칙은 불변(읽기 전용) 어레이에 대해 안전합니다.마찬가지로 쓰기 전용 어레이에 대해서도 반변 규칙이 안전합니다.

C#에서는 dynamic 키워드를 사용하여 duck 타이핑으로 어레이/컬렉션/generics를 조작할 수 있습니다.인텔리센스는 이렇게 손실되지만 동작합니다.

Java 및 C#의 공변 배열

Java 및 C#의 초기 버전에는 파라메트릭 다형이라고도 불리는 제네릭이 포함되어 있지 않았습니다.이러한 설정에서는 어레이를 불변하게 하면 유용한 다형 프로그램은 제외됩니다.

예를 들어, 어레이를 셔플하는 함수나 두 어레이가 동일한지 테스트하는 함수는Object.equals비바람에 의한 방법.구현은 어레이에 저장된 요소의 정확한 유형에 의존하지 않으므로 모든 유형의 어레이에서 작동하는 단일 함수를 쓸 수 있습니다.다음과 같은 유형의 기능을 쉽게 구현할 수 있습니다.

부울 등화 어레이(물건[] a1, 물건[] a2); 무효 셔플 어레이(물건[] a); 

다만, 어레이 타입이 불변으로 취급되고 있는 경우는, 이러한 함수를 정확하게 같은 타입의 어레이에서만 호출할 수 있습니다.Object[]예를 들어 문자열 배열을 섞을 수 없습니다.

따라서 Java와 C# 모두 배열 유형을 공변적으로 처리합니다.예를 들어 Java에서String[]의 서브타입입니다.Object[]C# 및string[]의 서브타입입니다.object[].

위에서 설명한 바와 같이 공변 어레이는 어레이에 대한 쓰기 문제로 이어집니다.Java 및 C#에서는 각 어레이 오브젝트를 작성할 때 타입으로 마킹함으로써 이 문제를 해결합니다.값이 어레이에 저장될 때마다 실행 환경은 값의 런타임 유형이 어레이의 런타임 유형과 동일한지 확인합니다.미스매치가 있는 경우,ArrayStoreException(자바) 또는ArrayTypeMismatchException(C#)가 느려집니다.

// a는 String의 단일 요소 배열입니다. 스트링[] a = 신규 스트링[1];  // b는 객체의 배열입니다. 물건[] b = a;  // 정수를 b에 할당합니다.이것은 만약 b가 정말로 다음과 같다면 가능할 것이다. // 객체의 배열이지만 실제로는 String의 배열이기 때문에 // java.discl을 가져옵니다.Array Store Exception(Array Store Exception). b[0] = 1; 

위의 예에서는 어레이(b)에서 안전하게 읽을 수 있습니다.어레이에 쓰려고 하면 문제가 발생할 수 있습니다.

이 접근법의 한 가지 단점은 엄격한 타입의 시스템이 컴파일 시에 검출할 수 있는 런타임에러 가능성이 남아 있다는 것입니다.또한 어레이에 쓸 때마다 런타임 체크가 추가로 필요하기 때문에 성능이 저하됩니다.

제네릭스의 추가에 의해 Java와 C#는 공분산에 의존하지 않고 이러한 종류의 다형함수를 쓸 수 있는 방법을 제공한다.어레이 비교 및 shuffling 함수는 파라미터화된 유형을 제공할 수 있습니다.

< >T> 부울 등화 어레이(T[] a1, T[] a2); < >T> 무효 셔플 어레이(T[] a); 

또는 C# 메서드가 읽기 전용으로 컬렉션에 액세스하도록 강제하기 위해 인터페이스를 사용할 수 있습니다.IEnumerable<object>배열 대신object[].

기능 타입

1급 기능을 가진 언어에는 고양이를 기대하고 동물을 돌려주는 기능(기재)과 같은 기능 유형이 있다.Cat -> AnimalOCaml 구문 또는Func<Cat,Animal>C# 구문).

또, 이러한 언어에서는, 어느 기능 타입이 다른 기능 타입의 서브 타입인 경우, 즉, 다른 타입의 기능을 기대하는 컨텍스트에서 어느 타입의 기능을 사용하는 것이 안전한지를 지정할 필요가 있습니다.f가 보다 일반적인 유형의 인수를 받아들여 g보다 더 구체적인 유형을 반환하는 경우 함수 g를 함수 f로 대체하는 것이 안전합니다.예를 들어, 유형의 함수Animal -> Cat,Cat -> Cat,그리고.Animal -> Animal어디에나 사용할 수 있다Cat -> Animal(이것을 커뮤니케이션의 강건성 원칙과 비교할 수 있습니다.「받아들이는 것은 자유롭고, 생산하는 것은 보수적이어야 한다」)일반적인 규칙은 다음과 같습니다.

1 ) ) ≤ P _ { 1 \ R _ { \ ( { } \ _ {} ) 1 2 \ P _ { } \ {} ) 、 [

추론 규칙 표기법을 사용하여 동일한 규칙을 다음과 같이 작성할 수 있습니다.

즉, → 유형 생성자는 매개 변수(입력) 유형에서는 반변하고 반환(출력) 유형에서는 공변량입니다. 규칙은 존 C에 의해 공식적으로 처음 언급되었다. 레이놀즈,[3] 그리고 루카 카델리[4]논문에서 더욱 유명해졌다.

함수를 인수로 사용하는 함수를 다룰 때 이 규칙을 여러 번 적용할 수 있습니다.예를 들어 규칙을 2회 적용하면 (( 1 ) ) ( ( 2 ) ) ( ( ( P { \ R ) \ R ) \ ( ( P _ { \ R ) \ R ) 2( ( P _ { 2 \ to R ) \ R) 복잡한 유형의 경우 특정 유형의 전문화가 안전한지 또는 안전하지 않은지를 정신적으로 추적하는 것은 혼란스러울 수 있지만, 어떤 위치가 동일하고 반대인지 쉽게 계산할 수 있습니다. 짝수 화살표의 왼쪽에 있는 위치는 공변적입니다.

객체 지향 언어의 상속

서브클래스가 슈퍼클래스의 메서드를 덮어쓸 경우 컴파일러는 덮어쓰기 메서드가 올바른 타입인지 확인해야 합니다.일부 언어에서는 유형이 슈퍼클래스(불분산)의 유형과 정확히 일치해야 하지만 재정의 메서드가 "더 나은" 유형을 가질 수 있도록 하는 것도 안전합니다.함수 유형의 일반적인 서브타이핑 규칙에 따르면 덮어쓰기 메서드는 보다 구체적인 유형(반환 유형 공분산)을 반환하고 보다 일반적인 인수(파라미터 유형 반전)를 받아들여야 합니다.UML 표기법에서는 다음과 같은 가능성이 있습니다(클래스 B는 슈퍼클래스인 클래스A를 확장하는 서브클래스입니다.

구체적인 예를 들어, 우리가 클래스 있는 동물 보호소에 대한 모델을 쓰고 있다.우리는 그것으로 가정하는Cat클래스의Animal그리고 우리가 기본 클래스(자바 구문을 사용하여) 가지고 있다.

UML diagram
학급 AnimalShelter {      동물 get Animal For Adoption 옵션() {         // ...     }          무효 풋애니멀(동물 동물) {         //...     } } 

자, 이제 질문은 다음과 같습니다. 만약 우리가 서브클래스로AnimalShelter에 어떤 타입을 지정할 수 있습니까?getAnimalForAdoption그리고.putAnimal?

공변법 반환 유형

공변 반환 유형을 허용하는 언어에서 파생 클래스는 다음 값을 덮어쓸 수 있습니다.getAnimalForAdoption보다 구체적인 유형을 반환하는 메서드:

UML diagram
학급 캣셸터 확장 동물 쉘터 {      고양이 get Animal For Adoption 옵션() {         돌아가다 신규 고양이();     } } 

주류 OO 언어 중 Java, C++ 및 C# (버전 9.0 이후)는 공변 반환 유형을 지원합니다.공변 반환 유형을 추가하는 것은 1998년 [6]표준 위원회가 승인한 C++ 언어의 첫 번째 수정 중 하나였다.스칼라D는 공변량 반환 유형도 지원합니다.

역변수 메서드 매개 변수 유형

마찬가지로 덮어쓰기 메서드가 기본 클래스의 메서드보다 일반적인 인수를 받아들일 수 있도록 하는 것이 안전합니다.

UML diagram
학급 캣셸터 확장 동물 쉘터 {     무효 풋애니멀(물건 동물성) {         // ...     } } 

이를 실제로 허용하는 개체 지향 언어는 몇 가지뿐입니다(예를 들어 mypy에서 type check 했을 때 Python 등).C++, Java 및 오버로드 및/또는 섀도우링을 지원하는 대부분의 다른 언어들은 오버로드 또는 섀도우 이름을 가진 메서드로 해석합니다.

그러나 Sather는 공분산과 반변성을 모두 지원했습니다.오버라이드된 메서드의 호출규칙은 out 파라미터와 반환값의 공변수이며, normal 파라미터의 경우(mode in의 경우)에 반합니다.

공변 방법 모수 유형

Effel[7] Dart라는 몇 가지 주류 언어를 사용하면 우선 메서드의 파라미터가 슈퍼클래스의 메서드(파라미터 타입 공분산)보다 더 구체적인 타입을 가질 수 있습니다.따라서 다음 Dart 코드는 check를 입력합니다.putAnimal기본 클래스의 메서드를 재정의합니다.

UML diagram
학급 캣셸터 확장 동물 쉘터 {      무효 풋애니멀(공변량 고양이 동물성) {         // ...     } } 

이것은 타입 세이프가 아닙니다.업캐스팅을 통해CatShelter에 대해서AnimalShelter개를 고양이 보호소에 놓아둘 수 있다.그것은 충족되지 않는다.CatShelter실행 시 오류가 발생합니다.타입의 안전성 결여(Effel 커뮤니티에서는 「catcall 문제」라고 불리고 있습니다.여기서 「cat」또는 「CAT」는 「변경된 가용성」또는 「타입」이라고 불립니다)는, 오랫동안 계속되어 온 문제입니다.수년간 글로벌 스태틱 분석, 로컬 스태틱 분석 및 새로운 언어 기능의 다양한 조합이 [8][9]제안되어 일부 에펠 컴파일러에서 구현되었습니다.

유형 안전성 문제에도 불구하고, 에펠 설계자들은 실제 요구 사항 모델링에 [9]중요한 공변 매개변수 유형을 고려한다.고양이 쉼터는 동물 쉼터의 일종이지만 추가적인 제약이 있으며, 이를 모델링하기 위해 유전과 제한된 매개변수 유형을 사용하는 것이 합리적인 것으로 보인다.이 상속의 사용을 제안함에 있어 에펠 설계자들은 하위 클래스의 객체는 항상 그들의 슈퍼 클래스의 객체보다 덜 제한되어야 한다는 리스코프 대체 원칙을 거부합니다.

메서드 파라미터의 공분산을 허용하는 주류 언어의 다른 한 가지 예는 클래스 생성자에 관한 PHP입니다.다음 예제에서는 메서드 파라미터가 부모의 메서드 파라미터에 공변하는 경우에도 __construct() 메서드가 허용됩니다.이 메서드가 __construct() 이외의 경우 오류가 발생합니다.

인터페이스 Animal Interface(Animal Interface) {}   인터페이스 도그 인터페이스 확장 Animal Interface(Animal Interface) {}   학급  용구 도그 인터페이스 {}   학급 애완동물 {     일반의 기능. __개요(Animal Interface(Animal Interface) 동물달러) {} }   학급 펫독 확장 애완동물 {     일반의 기능. __개요(도그 인터페이스 개값)     {         부모::__개요(개값);     } } 

공변량 매개변수가 유용한 것으로 보이는 또 다른 예는 소위 이진법, 즉 매개변수가 메서드가 호출되는 객체와 동일한 유형일 것으로 예상되는 방법입니다.예를 들어,compareTo방법:a.compareTo(b)체크할지를 확인합니다.a전후에 오다b순서도 있지만, 예를 들어 두 개의 유리수를 비교하는 방법은 두 개의 문자열을 비교하는 방법과 다릅니다.이진법의 다른 일반적인 예로는 등식 검정, 산술 연산, 부분 집합 및 합집합과 같은 집합 연산이 있습니다.

이전 버전의 Java에서는 비교 방법이 인터페이스로 지정되었습니다.Comparable:

인터페이스 동등한 {      인트 비교 대상(물건 o); } 

이 방법의 단점은 type 인수를 사용하도록 메서드가 지정되어 있다는 것입니다.Object. 표준 구현에서는 먼저 이 인수를 다운캐스트합니다(예상 타입이 아닌 경우 오류 발생).

학급 Rational Number(합리적인 번호) 용구 동등한 {     인트 분자;     인트 분모;     // ...       일반의 인트 비교 대상(물건 다른.) {         Rational Number(합리적인 번호) otherNum = (Rational Number(합리적인 번호))다른.;         돌아가다 정수.비교하다(분자 * otherNum.분모,                                otherNum.분자 * 분모);     } } 

공변 매개변수를 사용하는 언어에서 다음과 같은 인수compareTo원하는 유형을 직접 지정할 수 있습니다.RationalNumber(물론 이렇게 해도 실행 시 오류가 발생합니다.compareTo그 후, 예를 들어 a에 호출되었다.String.)

공변량 모수 유형이 필요하지 않음

다른 언어 기능은 Liskov 대체성을 유지하면서 공변 매개변수의 명백한 이점을 제공할 수 있습니다.

제네릭(일명 파라메트릭 다형성) 및 유계 정량화를 가진 언어에서는 앞의 예를 타입 세이프 [10]방식으로 작성할 수 있다.정의하는 대신AnimalShelter파라미터화된 클래스를 정의합니다.Shelter<T>(이것의 단점은 베이스 클래스의 실장자가 서브 클래스에 특화할 필요가 있는 타입을 예측할 필요가 있다는 것입니다.)

학급 주거지< >T 확장 동물> {      T get Animal For Adoption 옵션() {         // ...     }      무효 풋애니멀(T 동물성) {         // ...     } }       학급 캣셸터 확장 주거지< >고양이> {      고양이 get Animal For Adoption 옵션() {         // ...     }      무효 풋애니멀(고양이 동물) {         // ...     } } 

마찬가지로 Java의 최신 버전에서는Comparable인터페이스가 파라미터화되어 있기 때문에 다운캐스트를 타입 세이프 방식으로 생략할 수 있습니다.

학급 Rational Number(합리적인 번호) 용구 동등한< >Rational Number(합리적인 번호)> {      인트 분자;     인트 분모;     // ...               일반의 인트 비교 대상(Rational Number(합리적인 번호) otherNum) {         돌아가다 정수.비교하다(분자 * otherNum.분모,                                 otherNum.분자 * 분모);     } } 

도움이 되는 또 다른 언어 기능은 복수 디스패치입니다.바이너리 메서드가 쓰기 어려운 이유 중 하나는 다음과 같은 콜에서a.compareTo(b)올바른 실장 선택compareTo양쪽의 런타임 타입에 따라 달라집니다.a그리고.b단, 기존 OO 언어에서는 실행 시 유형만a가 고려됩니다.Common Lisp Object System(CLOS; 공통 리스프 오브젝트 시스템) 스타일의 복수 디스패치를 사용하는 언어에서는 비교 방법을 일반 함수로 기술할 수 있습니다.이 함수는 두 인수를 모두 사용하여 메서드를 선택할 수 있습니다.

Giuseppe[11] Castagna는 여러 디스패치가 있는 입력 언어에서는 범용 함수에 디스패치를 제어하는 파라미터와 그렇지 않은 "left-over" 파라미터가 있을 수 있음을 발견했습니다.메서드 선택 규칙은 가장 구체적인 메서드를 선택하므로 메서드가 다른 메서드를 덮어쓸 경우 덮어쓰기 메서드는 제어 파라미터에 대해 보다 구체적인 유형을 가집니다.한편, 타입의 안전성을 확보하기 위해서도, 언어에는 적어도 일반적인 파라미터와 같은 나머지 파라미터가 필요합니다.이전 용어를 사용하면 런타임 메서드 선택에 사용되는 유형은 공변하는 반면 메서드의 런타임 메서드 선택에 사용되지 않는 유형은 반변합니다.Java와 같은 기존의 단일 디스패치 언어도 이 규칙을 따릅니다.메서드 선택에 사용되는 인수는 1개뿐입니다(리시버 오브젝트는 숨겨진 인수로 메서드에 전달됩니다).this), 그리고 실제로 의 타입은this슈퍼클래스보다 우선 메서드 내부에서 더 전문화되어 있습니다.

Castagna는 공변량 모수 유형이 우수한 예(특히 이항 방법)를 다중 디스패치를 사용하여 처리해야 한다고 제안합니다. 다중 디스패치는 기본적으로 공변량입니다.그러나 대부분의 프로그래밍 언어에서는 여러 디스패치를 지원하지 않습니다.

분산 및 상속 요약

다음 표는 위에서 설명한 언어로 메서드를 재정의하는 규칙을 정리한 것입니다.

파라미터 타입 반환 유형
C++ (1998년 이후), Java (J2SE 5.0 이후), D 불변 공변량
C# 불변 공변량(C# 9 이후 - 불변량 이전)
스칼라, 사더 역변적 공변량
에펠 공변량 공변량

범용 타입

제네릭(파라메트릭 다형성)을 지원하는 프로그래밍 언어에서 프로그래머는 새로운 생성자를 사용하여 유형 시스템을 확장할 수 있습니다.예를 들어 다음과 같은 C# 인터페이스입니다.IList<T>다음과 같은 새로운 유형을 구축할 수 있습니다.IList<Animal>또는IList<Cat>그러면 이러한 유형 생성자의 분산이 어떻게 되어야 하는지 의문이 제기됩니다.

크게 두 가지 접근법이 있습니다.선언 사이트 분산 주석(예를 들어 C#)을 가진 언어에서 프로그래머는 유형 파라미터의 의도된 분산을 가진 범용 유형의 정의에 주석을 붙인다.사용 사이트 분산 주석(예: Java)을 사용하여 프로그래머는 대신 범용 유형이 인스턴스화된 위치에 주석을 달 수 있습니다.

선언 사이트 분산 주석

선언 사이트의 분산 주석이 있는 가장 일반적인 언어는 C#과 Kotlin입니다(키워드 사용).out그리고.in Scala 및 OCaml(키워드 사용)+그리고.-C# 에서는 인터페이스 타입에 대해서만 분산 주석을 사용할 수 있습니다.한편, Kotlin, Scala 및 OCaml에서는 인터페이스 타입과 구체적인 데이터 타입 모두에 대해 주석을 사용할 수 있습니다.

인터페이스

C#에서는 범용 인터페이스의 각 타입 파라미터에 공변량을 마크할 수 있습니다.out반대 ( )in또는 불변(주석 없음)입니다.예를 들어 인터페이스를 정의할 수 있습니다.IEnumerator<T>유형 매개 변수에서 공변량(출력)임을 선언합니다.

인터페이스 IENumerator< >나가. T> {     T 현재의 { 얻다; }     부울 [다음으로 이동(Move Next))(); } 

이 선언과 함께,IEnumerator유형 매개 변수에서 공변량으로 처리됩니다.IEnumerator<Cat>의 서브타입입니다.IEnumerator<Animal>.

타입 체커에서는 인터페이스의 각 메서드 선언이 타입 파라미터와 일치하도록 강제됩니다.in/out주석입니다.즉, 공변량으로 선언된 매개 변수는 반변위 위치(홀수 수의 반변형 유형 생성자 아래에서 발생할 경우 위치가 반변위)에서 발생해서는 안 됩니다.정확한[12][13] 규칙은 인터페이스 내의 모든 메서드의 반환 타입이 공변적으로 유효해야 하며 모든 메서드파라미터 타입이 반변적으로 유효해야 한다는 것입니다.여기서 유효한 S-ly는 다음과 같이 정의됩니다.

  • 일반적이지 않은 유형(클래스, 구조체, 에넘 등)은 공존 및 반변적으로 모두 유효하다.
  • 유형 파라미터T표시되지 않은 경우 공분산적으로 유효합니다.in마크가 붙어 있지 않은 경우는 무효가 됩니다.out.
  • 배열 유형A[]유효한 S-Ly의 경우A즉, C#에 공변 배열이 있기 때문입니다.
  • 범용형G<A1, A2, ..., An>각 파라미터에 대해 유효한 S-ly입니다.Ai,
    • Ai는 유효한 S-ly이며 ih 파라미터는G공변량으로 선언되거나
    • Ai는 유효(S가 아님)-ly이며 ih 파라미터는 다음과 같습니다.G위배된다고 선언된 경우 또는
    • Ai는 공변량 및 반변량 모두 유효하며 ih 매개 변수는 다음과 같습니다.G불변으로 선언됩니다.

이러한 규칙의 적용 예로서IList<T>인터페이스입니다.

인터페이스 리스트< >T> {     무효 삽입(인트 색인, T 아이템);     IENumerator< >T> Get Enumerator(); } 

파라미터 유형TInserttype 매개 변수와 같이 반비례하여 유효해야 합니다.T태그를 붙이면 안 됩니다.out마찬가지로 결과 유형도IEnumerator<T>GetEnumerator공변적으로 유효해야 한다. 즉, (이후)IEnumerator공변 인터페이스) 유형T공분산적으로 유효해야 합니다. 즉, type 파라미터T태그를 붙이면 안 됩니다.in이것은, 인터페이스가,IList공변치 또는 반변치 표시를 할 수 없습니다.

다음과 같은 일반적인 데이터 구조의 경우IList, 이러한 제약사항은,out매개 변수는 구조에서 데이터를 가져오는 메서드에만 사용할 수 있습니다.in파라미터는 데이터를 구조체에 넣는 메서드에만 사용할 수 있으므로 키워드를 선택할 수 있습니다.

데이터.

C#에서는 인터페이스의 파라미터에 대해 분산 주석을 사용할 수 있지만 클래스의 파라미터에 대해서는 사용할 수 없습니다.C# 클래스의 필드는 항상 변경 가능하기 때문에 C#의 가변 파라미터화 클래스는 그다지 유용하지 않습니다.그러나 불변의 데이터를 강조하는 언어는 공변 데이터 유형을 잘 활용할 수 있습니다.예를 들어 모든 Scala, KotlinOCaml에서 불변의 목록 유형은 공변입니다.List[Cat]의 서브타입입니다.List[Animal].

분산 주석을 확인하는 Scala의 규칙은 기본적으로 C#의 규칙과 동일합니다.그러나 특히 불변의 데이터 구조에 적용되는 몇 가지 용어가 있습니다.이러한 정의는 다음(에서 제외)에 나타나 있습니다.List[A]학급.

밀봉된 추상적인 학급 목록.[+A] 확장 AbstractSeq[A] {     방어하다 머리: A     방어하다 꼬리: 목록.[A]      /** 이 목록의 선두에 요소를 추가합니다.*/     방어하다 ::[B >: A] (x: B): 목록.[B] =         신규 스칼라.수집.불변의.::(x, 이것.)     /** ... */ } 

첫째, 변형 유형을 가진 클래스 구성원은 불변해야 합니다.여기서,head타입이 있다A공변량( )으로 선언되었습니다.+), 그리고 실제로head메서드로 선언되었습니다(def를 가변 필드로 선언하려고 합니다(var)는 타입 에러로서 거부됩니다.

둘째, 데이터 구조가 불변하더라도 파라미터 유형이 반대로 발생하는 메서드가 종종 있습니다.예를 들어 다음과 같은 방법을 생각해 봅시다.::이 명령어는 목록 맨 앞에 요소를 추가합니다.(실행은 유사한 이름의 클래스의 새 개체를 만드는 것으로 동작합니다. ::, 공백이 아닌 리스트의 클래스).가장 확실한 타입은

방어하다 :: (x: A): 목록.[A] 

그러나 이것은 유형 오류일 수 있습니다. 공변량 모수가A는 (함수 파라미터로서) 반변위치에 표시됩니다.하지만 이 문제를 피하기 위한 요령이 있다.증정하다::어떤 종류의 요소도 추가할 수 있는 보다 일반적인 유형B하는 한은B의 슈퍼타입입니다.A. 이는 다음 항목에 의존합니다.List공변성이기 때문에this타입이 있다List[A]그리고 우리는 그것을 활자를 가진 것으로 취급한다.List[B]·일반화된 타입은 언뜻 보기에는 건전하지 않을 수 있지만, 프로그래머가 간단한 타입 선언부터 시작하면, 타입 에러는 일반화할 필요가 있는 장소를 지적합니다.

분산추정리

컴파일러가 모든 데이터형 [14]파라미터에 대해 가능한 최선의 분산 주석을 자동으로 도출하는 유형 시스템을 설계할 수 있습니다.그러나 분석은 여러 가지 이유로 복잡해질 수 있습니다.첫째, 인터페이스의 분산 때문에 분석은 로컬이 아닙니다.I모든 인터페이스의 분산에 따라 달라집니다.I를 참조해 주세요.둘째, 고유한 최상의 솔루션을 얻기 위해 유형 시스템은 이변량 매개변수(동반 및 반변량)를 허용해야 한다.마지막으로 타입 파라미터의 분산은 인터페이스 설계자가 의도한 선택이지 단순히 일어나는 일이 아닙니다.

이러한 이유로[15] 대부분의 언어는 분산 추론을 거의 하지 않는다.C# 및 Scala는 분산 주석을 전혀 추론하지 않습니다.OCaml은 매개 변수화된 구체적인 데이터 유형의 분산을 추론할 수 있지만, 프로그래머는 추상 유형(인터페이스)의 분산을 명시적으로 지정해야 합니다.

예를 들어 OCaml 데이터 유형을 고려합니다.T기능을 감싼다.

유형 ('a, 'b) t = T  ('a -> 'b) 

컴파일러는 자동적으로 다음과 같이 추론합니다.T첫 번째 매개 변수에서는 반변하고 두 번째 매개 변수에서는 공변량입니다.프로그래머는 또한 명시적인 주석을 제공할 수 있으며, 컴파일러가 이를 만족하는지 확인할 것입니다.따라서 다음 선언은 앞의 선언과 동일합니다.

유형 (-'a, +'b) t = T  ('a -> 'b) 

인터페이스를 지정할 때 OCaml의 명시적 주석이 도움이 됩니다.예를 들어 표준 라이브러리 인터페이스Map.S연관표에는 지도 유형 생성자가 결과 유형에서 공변량임을 나타내는 주석이 포함됩니다.

모듈 유형 S =     시그니처         유형 열쇠         유형 (+'a) t          : 'a t          메모리: 열쇠 -> 'a t -> 부울         ...     끝. 

이를 통해 예를 들어 cat IntMap.t의 서브타입입니다.animal IntMap.t.

사용 사이트 분산 주석(와일드카드)

선언 사이트 접근법의 한 가지 단점은 다수의 인터페이스 유형을 불변으로 해야 한다는 것입니다.예를 들어 위에서 봤듯이IList그것이 두 가지를 포함했기 때문에 불변할 필요가 있었다.Insert그리고.GetEnumeratorAPI 설계자는 더 많은 편차를 노출하기 위해 사용 가능한 메서드의 서브셋을 제공하는 추가 인터페이스를 제공할 수 있습니다(예를 들어 "insert-only list"(삽입 전용 목록)Insert하지만 이는 곧 다루기 어려워집니다.

사용 사이트 분산은 유형이 사용될 코드의 특정 사이트에 주석으로 원하는 분산을 나타낸다.이것에 의해, 클래스의 유저는, 클래스의 설계자가 분산이 다른 복수의 인터페이스를 정의할 필요 없이, 서브 타이핑을 실시할 수 있습니다.대신, 범용 타입이 실제 파라미터화된 타입으로 인스턴스화되는 시점에서 프로그래머는 그 메서드의 서브셋만이 사용되는 것을 나타낼 수 있다.실제로 일반 클래스의 각 정의는 해당 클래스의 공변 및 반변 부분에 대한 인터페이스도 사용할 수 있게 합니다.

Java는 제한된 형식의 유계 실재형 와일드카드를 통해 사용 사이트 분산 주석을 제공합니다.매개 변수화된 유형은 와일드카드로 인스턴스화할 수 있습니다.?예를 들어, 상한 또는 하한과 함께 사용합니다.List<? extends Animal>또는List<? super Animal>. 무제한 와일드카드처럼List<?>와 동등하다List<? extends Object>이러한 유형은 다음을 나타냅니다.List<X>알 수 없는 타입으로X그 한계를 만족시키는 거죠예를 들어,l타입이 있다List<? extends Animal>타입 체커가 받아들입니다.

동물 a = l.얻다(3); 

왜냐하면 그 타입이기 때문에X의 서브타입으로 알려져 있다Animal,그렇지만

l.더하다(신규 동물()); 

에러 타입으로서 거부됩니다.Animal꼭 그런 것은 아닙니다.X. 일반적으로 일부 인터페이스는I<T>, 에 대한 참조I<? extends T>인터페이스로부터의 메서드 사용을 금지합니다.T는 메서드의 유형에 반하여 발생합니다.반대로 만약lhad 타입List<? super Animal>전화할 수 있다l.add하지만 아니다l.get.

Java의 와일드카드 서브타이핑은 큐브로 시각화할 수 있습니다.

Java의 비와일드카드 파라미터화 타입은 불변이지만 (예를 들어 서브타이핑 관계는 없습니다)List<Cat>그리고.List<Animal>), 와일드카드유형은 보다 엄격한 경계를 지정함으로써 구체화할 수 있습니다.예를들면,List<? extends Cat>의 서브타입입니다.List<? extends Animal>와일드카드 타입은 상한에서 공변(하한에서도 반변)임을 나타냅니다.합계하면 다음과 같은 와일드카드 타입이 지정됩니다.C<? extends T>서브유형을 형성하는 방법에는 3가지가 있습니다.클래스를 특화함으로써C, 보다 엄격한 경계를 지정함으로써T또는 와일드카드를 바꿈으로써?(그림 참조)을 참조).

위의 3가지 서브타이핑 중 2가지를 적용함으로써 예를 들어 타입의 인수를 전달할 수 있게 됩니다.List<Cat>기대하는 방법으로List<? extends Animal>이것은 공변 인터페이스 타입에서 발생하는 표현력의 종류입니다.종류List<? extends Animal>의 공변 메서드만을 포함하는 인터페이스 유형으로 동작합니다.List<T>단, 의 실장자는List<T>미리 정의할 필요가 없었습니다.

일반적인 데이터 구조의 경우IList, 공변 파라미터는 구조에서 데이터를 꺼내는 메서드에 사용되며 데이터를 구조체에 넣는 메서드에 대해서는 반변 파라미터가 사용됩니다.Joshua Bloch의 저서 Effective Java에 나오는 Producer Extends, Consumer Super(PECS)에 대한 니모닉은 공분산 및 위반을 언제 사용해야 하는지를 쉽게 기억할 수 있는 방법을 제공합니다.

와일드카드는 유연하지만 단점이 있습니다.use-site variance는 API 설계자가 인터페이스에 대한 유형 파라미터의 편차를 고려할 필요가 없음을 의미하지만 많은 경우 보다 복잡한 메서드시그니처를 사용해야 합니다.일반적인 예로는Comparable인터페이스입니다.집합에서 가장 큰 원소를 찾는 함수를 작성하려고 합니다.이 요소들은 다음과 같은 기능을 구현해야 합니다.compareTo첫 번째 도전은 다음과 같습니다.

< >T 확장 동등한< >T>> T 맥스.(수집< >T> ); 

단, 이 타입은 충분히 일반적이지 않습니다.즉, 이 타입의 최대값은Collection<Calendar>, 단,Collection<GregorianCalendar>문제는...GregorianCalendar실장하지 않다Comparable<GregorianCalendar>대신 (더 나은) 인터페이스Comparable<Calendar>자바에서는 C#과 달리Comparable<Calendar>의 서브타입으로 간주되지 않는다.Comparable<GregorianCalendar>. 대신 타입은max수정해야 합니다.

< >T 확장 동등한<? 잘 하는 군요 T>> T 맥스.(수집< >T> ); 

한정 와일드카드? super T라는 정보를 전달하다max다른 메서드만을 호출합니다.Comparable인터페이스입니다.이 예에서는 에 있는 모든 방법을 사용하고 있기 때문에 매우 곤란합니다.Comparable그 조건은 반변하기 때문에 3가지로 성립한다.선언 사이트 시스템은 의 정의에만 주석을 달아서 이 예를 덜 혼란스럽게 처리할 수 있습니다.Comparable.

선언 사이트 주석과 사용 사이트 주석 비교

사용 사이트 분산 주석을 사용하면 더 많은 프로그램이 유형 검사를 수행할 수 있도록 유연성이 향상됩니다.그러나, 이러한 언어들은 언어에 더해지는 복잡성으로 인해 복잡한 형식의 서명과 오류 메시지가 발생한다는 비판을 받아왔다.

추가 유연성이 유용한지 여부를 평가하는 한 가지 방법은 기존 프로그램에서 사용되는지 확인하는 것입니다.Java[14] 라이브러리의 대규모 세트를 조사한 결과 와일드카드 주석의 39%가 선언 사이트 주석으로 직접 대체될 수 있었습니다.따라서 나머지 61%는 Java가 use-site 시스템을 이용함으로써 이익을 얻는 장소를 나타냅니다.

선언 사이트 언어에서 라이브러리는 분산 노출을 줄이거나 더 많은 인터페이스를 정의해야 합니다.예를 들어, 스칼라 Collections 도서관은 공을 고용하고 있는 수업에:세개의 별도 인터페이스가 함께 변하는 기본 인터페이스 일반적인 방법이 담긴, side-effecting 메서드를 추가하는 고정 유위 전변의 버전 및 구조적인 공유를 이용하기 위한 상속된 구현 전문으로 할 수 있는 공변 불변의 버전을 정의합니다.[16]이 설계는 선언 사이트 주석과 함께 잘 작동하지만, 많은 수의 인터페이스는 라이브러리의 클라이언트에 복잡성 비용을 수반합니다.라이브러리 인터페이스를 변경하는 것은 선택사항이 아닙니다.특히 Java에 제네릭을 추가할 때 한 가지 목표는 바이너리 하위 호환성을 유지하는 것이었습니다.

한편 Java 와일드카드 자체는 복잡합니다.콘퍼런스[17] 프레젠테이션에서 조슈아 블로흐는 폐쇄에 대한 지원을 추가할 때 "우리는 다른 와일드카드를 살 여유가 없다"고 말하며 그들을 이해하고 사용하는 것이 너무 어렵다고 비판했다.Scala의 초기 버전은 사용 사이트 분산 주석을 사용했지만 프로그래머는 실제로 사용하기 어렵다는 것을 알게 된 반면 선언 사이트 [18]주석은 클래스를 설계할 때 매우 유용한 것으로 밝혀졌다.이후 버전의 Scala는 Java 스타일의 실존 유형과 와일드카드를 추가했지만, Martin Odersky에 따르면 Java와의 상호 운용성이 필요하지 않았다면 이러한 유형은 [19]포함되지 않았을 것이다.

Ross[20] Tate는 Java 와일드카드의 복잡성의 일부는 실존적 유형의 형식을 사용하여 use-site variance를 인코딩하는 결정 때문이라고 주장한다.원래[21][22] 제안서는 분산 주석, 작성에 특수 목적 구문을 사용했다.List<+Animal>자바의 장황한 표현 대신List<? extends Animal>.

와일드카드는 실존 타입의 한 형태이기 때문에 분산뿐만 아니라 더 많은 용도로 사용할 수 있습니다.같은 타입List<?>('알 수 없는 유형의 목록')[23]을 사용하면 유형 매개 변수를 정확하게 지정하지 않고도 개체를 메서드에 전달하거나 필드에 저장할 수 있습니다.이것은 특히 다음과 같은 수업에서 중요합니다.Class여기서 대부분의 메서드는 type 파라미터를 언급하지 않습니다.

그러나 실존 유형에 대한 유형 추론은 어려운 문제이다.컴파일러 실장자에게 Java 와일드카드는 타입 체커 종료, 타입 인수 추론 및 모호한 [24]프로그램에 관한 문제를 일으킵니다.일반적으로 제네릭스를 사용하는 Java 프로그램이 잘 입력되었는지 [25]여부를 판단할 수 없기 때문에 어떤 타입 체커는 무한 루프에 들어가거나 일부 프로그램에 대해 타임아웃해야 합니다.프로그래머의 경우 복잡한 유형의 오류 메시지로 이어집니다.Java 유형은 와일드카드를 새로운 유형 변수(이른바 캡처 변환)로 대체하여 와일드카드 유형을 확인합니다.이는 오류 메시지가 프로그래머가 직접 작성하지 않은 유형 변수를 참조하기 때문에 오류 메시지를 읽기 어렵게 만들 수 있습니다.예를 들어, 를 추가하려고 합니다.Cat에 대해서List<? extends Animal>다음과 같은 오류가 발생합니다.

메서드 List.add(capture#1)는 적용되지 않습니다(실제 인수는 메서드 호출 변환에 의해 capture#1로 변환할 수 없습니다).여기서 capture#1은 새로운 유형 변수입니다.capture#1 extends Animal from ?는 동물입니다.

선언 사이트 주석과 사용 사이트 주석 모두 유용할 수 있으므로 일부 유형 시스템은 둘 [14][20]다 제공한다.

어원학

이러한 용어는 범주 이론의 공변 함수반변 함수 개념에서 유래합니다.오브젝트가 유형이고 모형이 서브타입 관계를 나타내는 C(\ C 생각해 보겠습니다(이것은 부분적으로 순서가 매겨진 세트를 카테고리로 간주할 수 있는 예를 나타냅니다).예를 들어 함수 타입 컨스트럭터는 p와 r의 2가지 타입을 취하여 새로운 타입 p → r을 작성합니다.따라서 C C 오브젝트를 C C의 오브젝트로 가져옵니다.이 조작은 첫 번째 파라미터에 대해 서브타이핑규칙에 의해 for을 반전시켜 두 번째 파라미터로 유지합니다.반전됩니다.첫 번째 매개 변수에는 iant 펑터가 있고 두 번째 매개 변수에는 공변량 펑터가 있습니다.

「 」를 참조해 주세요.

레퍼런스

  1. ^ 이것은 병적인 경우에만 발생합니다.예를들면,type 'a t = int: 모든 타입을 사용할 수 있습니다.'a그리고 결과는 여전히int[검증 필요]
  2. ^ Func <T, TResult> 위임 - MSDN 문서
  3. ^ Reynolds, John C. (1981). The Essence of Algol. Symposium on Algorithmic Languages. North-Holland.
  4. ^ Cardelli, Luca (1984). A semantics of multiple inheritance (PDF). Semantics of Data Types (International Symposium Sophia-Antipolis, France, June 27–29, 1984). Lecture Notes in Computer Science. Vol. 173. Springer. pp. 51–67. doi:10.1007/3-540-13346-1_2. ISBN 3-540-13346-1.
    긴 버전:
  5. ^ Torgersen, Mads. "C# 9.0 on the record".
  6. ^ Allison, Chuck. "What's New in Standard C++?".
  7. ^ "Fixing Common Type Problems". Dart Programming Language.
  8. ^ Bertrand Meyer (October 1995). "Static Typing" (PDF). OOPSLA 95 (Object-Oriented Programming, Systems, Languages and Applications), Atlanta, 1995.
  9. ^ a b Howard, Mark; Bezault, Eric; Meyer, Bertrand; Colnet, Dominique; Stapf, Emmanuel; Arnout, Karine; Keller, Markus (April 2003). "Type-safe covariance: Competent compilers can catch all catcalls" (PDF). Retrieved 23 May 2013.
  10. ^ Franz Weber (1992). "Getting Class Correctness and System Correctness Equivalent - How to Get Covariance Right". TOOLS 8 (8th conference on Technology of Object-Oriented Languages and Systems), Dortmund, 1992. CiteSeerX 10.1.1.52.7872.
  11. ^ Castagna, Giuseppe (May 1995). "Covariance and contravariance: conflict without a cause". ACM Transactions on Programming Languages and Systems. 17 (3): 431–447. CiteSeerX 10.1.1.115.5992. doi:10.1145/203095.203096.
  12. ^ Lippert, Eric (3 December 2009). "Exact rules for variance validity". Retrieved 16 August 2016.
  13. ^ "Section II.9.7". ECMA International Standard ECMA-335 Common Language Infrastructure (CLI) (6th ed.). June 2012.
  14. ^ a b c Altidor, John; Shan, Huang Shan; Smaragdakis, Yannis (2011). "Taming the wildcards: combining definition- and use-site variance". Proceedings of the 32nd ACM SIGPLAN conference on Programming language design and implementation (PLDI'11). ACM. pp. 602–613. CiteSeerX 10.1.1.225.8265. doi:10.1145/1993316.1993569. ISBN 9781450306638.{{cite conference}}: CS1 유지보수: 날짜 및 연도(링크)
  15. ^ Lippert, Eric (October 29, 2007). "Covariance and Contravariance in C# Part Seven: Why Do We Need A Syntax At All?". Retrieved 16 August 2016.
  16. ^ Odersky, Marin; Spoon, Lex (September 7, 2010). "The Scala 2.8 Collections API". Retrieved 16 August 2016.
  17. ^ Bloch, Joshua (November 2007). "The Closures Controversy [video]". Presentation at Javapolis'07. Archived from the original on 2014-02-02.{{cite web}}: CS1 유지보수: 위치(링크)
  18. ^ Odersky, Martin; Zenger, Matthias (2005). "Scalable component abstractions" (PDF). Proceedings of the 20th annual ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '05). ACM. pp. 41–57. CiteSeerX 10.1.1.176.5313. doi:10.1145/1094811.1094815. ISBN 1595930310.
  19. ^ Venners, Bill; Sommers, Frank (May 18, 2009). "The Purpose of Scala's Type System: A Conversation with Martin Odersky, Part III". Retrieved 16 August 2016.
  20. ^ a b Tate, Ross (2013). "Mixed-Site Variance". FOOL '13: Informal Proceedings of the 20th International Workshop on Foundations of Object-Oriented Languages. CiteSeerX 10.1.1.353.4691.
  21. ^ Igarashi, Atsushi; Viroli, Mirko (2002). "On Variance-Based Subtyping for Parametric Types". Proceedings of the 16th European Conference on Object-Oriented Programming (ECOOP '02). Lecture Notes in Computer Science. Vol. 2374. pp. 441–469. CiteSeerX 10.1.1.66.450. doi:10.1007/3-540-47993-7_19. ISBN 3-540-47993-7.
  22. ^ Thorup, Kresten Krab; Torgersen, Mads (1999). "Unifying Genericity: Combining the Benefits of Virtual Types and Parameterized Classes". Object-Oriented Programming (ECOOP '99). Lecture Notes in Computer Science. Vol. 1628. Springer. pp. 186–204. CiteSeerX 10.1.1.91.9795. doi:10.1007/3-540-48743-3_9. ISBN 3-540-48743-3.{{cite conference}}: CS1 유지보수: 날짜 및 연도(링크)
  23. ^ "The Java™ Tutorials, Generics (Updated), Unbounded Wildcards". Retrieved July 17, 2020.
  24. ^ Tate, Ross; Leung, Alan; Lerner, Sorin (2011). "Taming wildcards in Java's type system". Proceedings of the 32nd ACM SIGPLAN conference on Programming language design and implementation (PLDI '11). pp. 614–627. CiteSeerX 10.1.1.739.5439. ISBN 9781450306638.
  25. ^ Grigore, Radu (2017). "Java generics are turing complete". Proceedings of the 44th ACM SIGPLAN Symposium on Principles of Programming Languages (POPL'17). pp. 73–85. arXiv:1605.05274. Bibcode:2016arXiv160505274G. ISBN 9781450346603.

외부 링크