[java] Map.get (Object key)이 (완전히) 일반이 아닌 이유는 무엇입니까

무엇의 인터페이스에서 완전히 일반적인 get 메소드를 가지고 있지하는 결정 뒤에 이유가 있습니다 java.util.Map<K, V>.

질문을 명확히하기 위해 메소드의 서명은

V get(Object key)

대신에

V get(K key)

왜 그런지 궁금 remove, containsKey, containsValue합니다.



답변

다른 사람들이 언급했듯이, get()검색하는 항목의 키가 전달하는 객체와 동일한 유형일 필요가 없기 때문에, 등이 일반적인 이유는 아닙니다 get(). 방법의 스펙은 그것들이 동일 할 필요가있다. 이는 equals()메소드가 오브젝트와 동일한 유형이 아니라 오브젝트를 매개 변수로 사용 하는 방법 에서 비롯 됩니다.

많은 클래스가 equals()자신의 객체가 자신의 클래스의 객체와 같을 수 있도록 정의한 것은 일반적으로 사실이지만 , Java에는 그렇지 않은 곳이 많이 있습니다. 예를 들어,에 대한 사양은 List.equals()두 List 객체가 모두 List이고 서로 다른 구현 인 경우에도 동일한 내용을 갖는 경우 동일하다고 말합니다 List. 그래서 방법의 사양에 따라,이 문제의 예를 다시 오는 것은을 가질 수 있습니다 Map<ArrayList, Something>나를 호출 할 수 get()로모그래퍼 LinkedList인수로하고,이 같은 내용 목록입니다 키를 검색합니다. get()일반적이고 인수 유형이 제한되어 있으면 불가능합니다 .


답변

Google의 멋진 Java 코더 인 Kevin Bourrillion 은 얼마 전에 블로그 게시물 에이 문제에 대해 정확히 썼습니다 (확실히 Set대신 Map). 가장 관련성이 높은 문장 :

통일적으로, Java Collections Framework (및 Google Collections Library)의 메소드는 콜렉션이 손상되는 것을 방지해야하는 경우를 제외하고는 매개 변수 유형을 제한하지 않습니다.

.NET은 올바른 키 유형을 요구하는 것이 좋을 것 같습니다.하지만 블로그 게시물의 추론을 따르는 것이 좋습니다. .NET에 대해 언급했지만 .NET에서 문제가되지 않는 이유 중 일부는 .NET에서 차이가 더 제한적이므로 더 문제 가 있음을 설명하는 것이 좋습니다.)


답변

계약은 다음과 같이 표현됩니다.

보다 공식적으로,이 맵에 키 k에서 값 v 로의 매핑이 포함되어 있으면 (key == null? k == null :
key.equals (k) )이 메소드는 v를 반환합니다. 그렇지 않으면 null을 반환합니다. 이러한 매핑은 최대 하나만있을 수 있습니다.

(내 강조)

따라서 성공적인 키 조회는 입력 키의 동등성 방법 구현에 따라 다릅니다. 반드시 k의 클래스에 의존 하지는 않습니다 .


답변

그것은 Postel ‘s Law의 적용이다. “당신이하는 일에 보수적이며, 다른 사람들로부터 받아 들여지는 것에있어 자유 롭다.”

유형에 관계없이 평등 검사를 수행 할 수 있습니다. 이 equals메소드는 Object클래스 에 정의 Object되어 있으며 매개 변수로 허용합니다 . 따라서 키 동등성 및 키 동등성 기반 조작이 모든 Object유형 을 승인하는 것이 좋습니다.

맵이 키 값을 반환하면 type 매개 변수를 사용하여 가능한 한 많은 유형 정보를 보존합니다.


답변

나는 Generics Tutorial 의이 섹션에서 상황을 설명한다고 생각합니다 (내 강조).

“일반 API가 지나치게 제한적이지 않아야합니다. API의 원래 계약을 계속 지원해야합니다. java.util.Collection의 예제를 다시 고려하십시오. 일반적인 API는 다음과 같습니다.

interface Collection {
  public boolean containsAll(Collection c);
  ...
}

그것을 생성하려는 순진한 시도는 다음과 같습니다.

interface Collection<E> {
  public boolean containsAll(Collection<E> c);
  ...
}

이것은 확실히 유형 안전하지만 API의 원래 계약을 준수하지는 않습니다.
containsAll () 메서드는 모든 종류의 들어오는 컬렉션에서 작동합니다. 들어오는 컬렉션에 실제로 E 인스턴스 만 포함 된 경우에만 성공하지만 다음과 같습니다.

  • 들어오는 컬렉션의 정적 형식은 호출자가 전달되는 컬렉션의 정확한 형식을 알지 못하거나 컬렉션이 Collection <S>이고 S가 E의 하위 유형이기 때문에 다를 수 있습니다.
  • 다른 유형의 컬렉션으로 containsAll ()을 호출하는 것이 합법적입니다. 이 루틴은 작동해야하며 false를 반환합니다. “

답변

그 이유는 밀폐에 의해 결정된다는 것이다 equalshashCode그 위에있는 방법 Object및 가지고 두 Object파라미터. 이것은 Java 표준 라이브러리의 초기 설계 결함이었습니다. Java 유형 시스템의 제한 사항과 함께 equals 및 hashCode에 의존하는 모든 것을 강제로 가져옵니다 Object.

자바 형태 보증 된 해시 테이블과 평등을 할 수있는 유일한 방법은 삼가고하는 것입니다 Object.equalsObject.hashCode및 일반 대체를 사용합니다. 기능 자바는 바로 이러한 목적을 위해 형 클래스와 함께 제공 : Hash<A>Equal<A>. 래퍼 HashMap<K, V>소요가 제공된다 Hash<K>Equal<K>생성자입니다. 따라서이 클래스 getcontains메소드는 유형의 일반 인수를 사용합니다 K.

예:

HashMap<String, Integer> h =
  new HashMap<String, Integer>(Equal.stringEqual, Hash.stringHash);

h.add("one", 1);

h.get("one"); // All good

h.get(Integer.valueOf(1)); // Compiler error


답변

적합성.

제네릭을 사용할 수 있기 전에 get (Object o) 만있었습니다.

그들이이 방법을 get (<K> o)으로 변경했다면 작업 코드를 다시 컴파일하기 위해 Java 사용자에게 대규모 코드 유지 관리를 강제했을 것입니다.

그들은 도입 된 한 추가 방법을 get_checked 말 (<K> O)과 온화한 전환 경로가 있었다, 그래서 이전의 get () 메소드를 중단합니다. 그러나 어떤 이유로 든 이것이 이루어지지 않았습니다. 현재 상황은 get () 인수와지도의 선언 된 키 유형 <K> 사이의 유형 호환성을 확인하기 위해 findBugs와 같은 도구를 설치해야한다는 것입니다.

.equals ()의 의미와 관련된 인수는 가짜입니다. (기술적으로는 정확하지만 여전히 가짜라고 생각합니다. o1과 o2에 공통 수퍼 클래스가없는 경우 올바른 마음의 어떤 ​​디자이너도 o1.equals (o2)를 적용하지 않습니다.)