배열은 공변적이지만 일반 분포는 불변인 이유는 무엇입니까?
Joshua Bloch의 Effective Java에서
- 어레이는 두 가지 중요한 점에서 범용 타입과 다릅니다.첫 번째 배열은 공변량입니다.제네릭스는 불변입니다.
공변량은 단순히 X가 Y의 하위 유형이면 X[]도 Y의 하위 유형임을 의미합니다.배열은 공변량 문자열은 객체 So의 하위 유형입니다.
String[] is subtype of Object[]
불변이란 단순히 X가 Y의 아형인지 아닌지에 관계없이,
List<X> will not be subType of List<Y>.
왜 Java에서 어레이를 공변화하기로 결정했는지 궁금하네요."왜 어레이는 불변인가?"와 같은 다른 SO 게시물이 있지만, 그것들은 Scala에 초점을 맞추고 있는 것 같아서 나는 이해할 수 없다.
Wikipedia 경유:
Java 및 C#의 초기 버전에는 제네릭(파라메트릭 다형성)이 포함되어 있지 않았습니다.
이러한 설정에서는 어레이를 불변하게 하면 유용한 다형 프로그램은 제외됩니다.를 사용하는 .
Object.equals
츠요시구현은 어레이에 저장된 요소의 정확한 유형에 의존하지 않으므로 모든 유형의 어레이에서 작동하는 단일 함수를 쓸 수 있습니다.의 수 .boolean equalArrays (Object[] a1, Object[] a2); void shuffleArray(Object[] a);
, 이러한 함수를 할 수 은, 하게 「」, 「」, 「」, 「」, 「」, 「」의 타입의 입니다.
Object[]
예를 들어 문자열 배열을 섞을 수 없습니다.따라서 Java와 C# 모두 배열 유형을 공변적으로 처리합니다.를 들어 #의 C#의 경우
string[]
는 의 입니다.object[]
Java 에서는, 「 」String[]
는 의 입니다.Object[]
.
이는 "배열은 왜 공변인가?"라는 질문에 답하거나 더 정확하게 "배열은 왜 당시에 공변으로 만들어졌는가?"라는 질문에 답합니다.
제네릭스가 도입되었을 때, Jon Sket이 이 답변에서 지적한 이유로 의도적으로 공변량을 만들지 않았습니다.
ㅇㅇㅇㅇ, ᄋ.
List<Dog>
아니다List<Animal>
"로할 수 해 보십시오.List<Animal>
할 수 - 동물도...양이고이제, 당신은 논리적으로 강아지 한 마리에 고양이를 추가할 수 있나요?절대로 그렇지 않아요.// Illegal code - because otherwise life would be Bad List<Dog> dogs = new List<Dog>(); List<Animal> animals = dogs; // Awooga awooga animals.add(new Cat()); Dog dog = dogs.get(0); // This should be safe, right?
갑자기 당신은 매우 혼란스러운 고양이를 갖게 되었다.
Wikipedia 문서에서 설명한 배열의 공분산(및 반분산) 표현을 와일드카드로 가능케 했기 때문에 배열을 공분산(및 반분산)으로 만든 원래 동기는 일반학에는 적용되지 않았습니다.
boolean equalLists(List<?> l1, List<?> l2);
void shuffleList(List<?> l);
그 이유는 모든 어레이가 실행 시 요소 유형을 알고 있지만 일반 컬렉션은 유형 삭제로 인해 요소 유형을 인식하지 못하기 때문입니다.
예를 들어 다음과 같습니다.
String[] strings = new String[2];
Object[] objects = strings; // valid, String[] is Object[]
objects[0] = 12; // error, would cause java.lang.ArrayStoreException: java.lang.Integer during runtime
이것이 범용 컬렉션에서 허용되는 경우:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings; // let's say it is valid
objects.add(12); // invalid, Integer should not be put into List<String> but there is no information during runtime to catch this
그러나 이 경우 나중에 누군가가 목록에 액세스하려고 할 때 문제가 발생할 수 있습니다.
String first = strings.get(0); // would cause ClassCastException, trying to assign 12 to String
도움이 될 수 있습니다.-
제네릭이 공변량이 아닙니다.
Number를 될 Integer도 Java가 .Integer number Number 、 Integer number Number number [ ]의 정수입니다.Number[]
'이나 '할당'은 롭게 할 수 Integer[]
Number[]
Number가 Integer의 슈퍼타입일 경우).Number[]
은 ★★★★★★★★★★★입니다.Integer[]
할 수 즉, . ) 범 as as as as as as as as as as as as as as as as as . . . . . . . . . . . . . . 。List<Number>
은 ★★★★★★★★★★★입니다.List<Integer>
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , .List<Integer>
List<Number>
아쉽게도 되지 않아요★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
그런 식으로 작동하지 않는 데는 그만한 이유가 있는 것으로 나타났습니다.그것은 안전 장치들이 제공하기로 되어 있던 타입을 망가뜨릴 것이다. 여러분이 '를 붙일 수 .List<Integer>
a까지List<Number>
에서는 '가 아닌 을 '정수'에 수 List<Integer>
:
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
왜냐하면 ln은List<Number>
플로트를 추가하는 것은 완전히 합법적인 것 같습니다., 이 ln과 있는 li
그러면, 그것은 li의 정의에 내포된 형식 안전 약속을 깨게 될 것이다. 정수의 리스트이기 때문에, 일반 유형은 공변할 수 없다.
배열은 다음 두 가지 이상의 이유로 공변합니다.
공변량으로 변경되지 않는 정보를 보관하는 수집에 유용합니다.T의 집합이 공변량이 되려면 백업 저장소도 공변량이어야 합니다.의 '불변'을 할 수
T
「」를 하지 않은 .T[]
백업 스토어(트리 또는 링크 리스트 사용 등)로서 이러한 컬렉션은 어레이에 의해 백업되는 컬렉션과 마찬가지로 동작하지 않을 것입니다.공변량 불변 컬렉션에 대해 제공하는 더 나은 방법은 백업 저장소를 사용할 수 있는 "공변량 불변 배열" 유형을 정의하는 것이라고 주장할 수 있지만 배열 공분산을 허용하는 것이 아마도 더 쉬웠을 것입니다.어레이는 코드에 의해 자주 변환됩니다.코드에 의해 변환됩니다.코드는 어떤 타입이 포함될지 알 수 없지만 같은 어레이에서 읽어내지 못한 것은 어레이에 추가되지 않습니다.이것의 대표적인 예가 정렬 코드입니다.개념적으로 어레이 유형에는 요소를 스왑 또는 허용하는 메서드가 포함되거나(이러한 메서드는 모든 어레이 유형에 동일하게 적용 가능), 어레이 및 어레이에서 읽어낸1개 또는 여러 항목에 대한 참조를 유지하는 "어레이 조작기" 개체를 정의할 수 있으며 이전에 읽은 항목을 저장하는 메서드를 포함할 수 있습니다.자기들이 온 열로 만들어 버렸죠배열이 공변하지 않으면 사용자 코드는 이러한 유형을 정의할 수 없지만 런타임에는 몇 가지 특수한 메서드가 포함될 수 있습니다.
배열이 공변적이라는 사실은 추악한 해킹으로 간주될 수 있지만, 대부분의 경우 작동 코드 작성을 용이하게 합니다.
구조에서 , 예를 다형 알고리즘을 기술할 수 것입니다.Arrays.sort()
.
제네릭스의 경우, 다음과 같은 와일드카드 타입을 사용할 수 있습니다.
<E extends Comparable<E>> void sort(E[]);
와일드카드 타입에는 와일드카드 캡처가 필요하며, 이를 위해서는 타입 파라미터의 개념이 필요합니다.어레이가 Java에 추가되었을 때는 이 중 어느 것도 사용할 수 없었습니다.또, 참조 타입의 공변량의 어레이를 만드는 것으로, 폴리모픽 알고리즘을 가능하게 하는 보다 간단한 방법이 가능하게 되었습니다.
void sort(Comparable[]);
그러나 이러한 단순성으로 인해 정적 유형 시스템에 다음과 같은 허점이 생겼습니다.
String[] strings = {"hello"};
Object[] objects = strings;
objects[0] = 1; // throws ArrayStoreException
참조 유형 배열에 대한 모든 쓰기 액세스에 대한 런타임 검사가 필요합니다.
간단히 말해 제네릭스에 의해 구현된 새로운 접근 방식은 유형 시스템을 더욱 복잡하게 만들 뿐만 아니라 정적으로도 더 안전하게 만들지만, 오래된 접근 방식은 더 단순하고 정적으로 덜 안전합니다.언어 설계자들은 문제를 거의 일으키지 않는 활자 시스템의 작은 허점을 메우는 것보다 더 중요한 일을 하는 단순한 접근법을 택했다.나중에 Java가 확립되고 긴급한 요구가 해결되었을 때 범용 기기용으로 적절하게 실행할 수 있는 리소스가 있었습니다(그러나 어레이용으로 변경하면 기존 Java 프로그램이 망가집니다).
애초에 그들이 잘못된 결정을 해서 배열이 공변하게 된 것 같아요.여기서 설명한 바와 같이 타입의 안전성이 깨지고 하위 호환성으로 인해 해당 타입의 안전성에 문제가 생겼으며, 이후 범용에 대해 동일한 실수를 하지 않으려고 노력했습니다.그리고 그것이 조슈아 블로치가 "유효한 자바 (제2판)"의 25번 항목에서 목록보다 목록을 선호하는 이유 중 하나이다.
서브타이핑은 범용 타입으로 확장되지 않습니다.는 T <: U라고 하다>를 하는 것이 .
C<T>
C<U>
몇 가면 는 'JLS로 하다'라고 설명하고 .
배열은 공변량(첫 번째 글머리 기호):
4.10.3 어레이 유형별 서브타이핑
의견: A를 상정하고 때 B요소를 썼을 입니다.코드가 어레이 A[]를 상정하고 있을 때 B[]를 지정하면 B는 A의 서브클래스이며, 여기서 고려해야 할 것은 어레이 요소를 읽을 때의 동작과 그것을 쓸 때의 동작뿐입니다. 모든 대해 하는 것은 않습니다은 '유형 안전'이 유지되도록 하는 입니다).ArrayStoreException
A[]B[ ] a a a a a a a a a a a a a a a a a a a a a a a a a. 선언 시 " " " 입니다.SomeClass<T>
수 있습니다.T
수업에서 사용하는데, 가능한 모든 조합을 알아내서 언제 허용되고 언제 허용되지 않는지에 대한 규칙을 작성하는 것은 너무 복잡하다고 생각합니다.
쓰면 안 요.List<Object> l = new ArrayList<String>();
자바가 런타임 예외로부터 우리를 보호하려고 하기 때문입니다.안 하실 수도 있어요.Object[] o = new String[0];
않다 렇지지않않않않합니다.
Integer[] numbers = { new Integer(42)};
Object[] objects = numbers;
objects[0] = "forty two"; // throws ArrayStoreException
코드는 컴파일되지만 실행 시 예외가 발생합니다.어레이의 경우 Java는 어레이에서 허용되는 유형을 인식합니다. 「이행」이 되어 있기 에,Integer[]
Object[]
Integer[]
.
유형 삭제로 인해 Array List에 대한 보호는 없습니다.실행 시 ArrayList는 무엇이 허용되는지 알 수 없습니다.따라서 Java는 컴파일러를 사용하여 이 상황이 발생하지 않도록 합니다.그렇다면 Java는 왜 이 지식을 Array List에 추가하지 않는 걸까요?그 이유는 하위 호환성 때문입니다. 즉, Java는 기존 코드를 파괴하지 않기 때문입니다.
OCP 레퍼런스
언급URL : https://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant
'programing' 카테고리의 다른 글
Vue CLI: 모듈 '../package를 찾을 수 없습니다.npm 설치 후 json 오류 발생 (0) | 2022.07.28 |
---|---|
다른 컴포넌트에서 App.vue에 액세스하는 방법 (0) | 2022.07.28 |
SQLite의 초당 삽입 성능 향상 (0) | 2022.07.28 |
글로벌 스타일에 영향을 주지 않고 VueJS 컴포넌트로 이메일 HTML 렌더링 (0) | 2022.07.28 |
상태가 200 OK인 동안 Axios가 네트워크 오류를 전송함 (0) | 2022.07.28 |