Java Collections가 일반적인 메소드를 제거하지 않는 이유는 무엇입니까? Collection.remove (Object o)가 일반

Collection.remove (Object o)가 일반 이 아닌 이유는 무엇 입니까?

Collection<E>할 수있는 것 같습니다boolean remove(E o);

그런 다음 실수 Set<String>로 각 개별 문자열 대신 (예 :)을 제거하려고하면 Collection<String>나중에 디버깅 문제 대신 컴파일 시간 오류가 발생합니다.



답변

Josh Bloch와 Bill Pugh는 Java Puzzlers IV : Phantom Reference Menace, Clone의 Attack 및 Revenge of the Shift 에서이 문제를 언급합니다 .

조쉬 블로흐 (Josh Bloch)는 (6:41) 맵의 get 메소드를 생성하고 메소드 및 기타를 제거하려고 시도했지만 “단순히 작동하지 않았다”고 말합니다.

콜렉션의 일반 유형 만 매개 변수 유형으로 허용하는 경우 생성 할 수없는 합리적인 프로그램이 너무 많습니다. 그에게 주어진 예는의 교차로 ListNumber의와
ListLong의.


답변

remove()( Map및뿐만 아니라 Collection)은 모든 유형의 객체를에 전달할 수 있어야하기 때문에 일반적이지 않습니다 remove(). 제거 된 객체는 전달한 객체와 동일한 유형일 필요는 없습니다 remove(). 단지 동일해야합니다. 의 사양에서 remove(), remove(o)개체 제거 e(o==null ? e==null : o.equals(e))입니다 true. 필요 아무것도 없다는 것을 유의 oe같은 유형이 될 수 있습니다. 이는 equals()메소드가 Object오브젝트와 동일한 유형이 아니라 as 매개 변수를 사용 한다는 사실에서 비롯 됩니다.

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


답변

type 매개 변수가 와일드 카드 인 경우 일반 remove 메소드를 사용할 수 없습니다.

Map의 get (Object) 메소드를 사용 하여이 질문에 부딪힌 것을 기억합니다. 이 경우 get 메소드는 일반적이지 않지만, 첫 번째 유형 매개 변수와 동일한 유형의 오브젝트가 전달 될 것으로 예상해야합니다. 와일드 카드를 첫 번째 유형 매개 변수로 사용하여지도를 전달하는 경우 해당 인수가 일반적인 경우 해당 메소드를 사용하여지도에서 요소를 가져올 수있는 방법이 없다는 것을 깨달았습니다. 컴파일러가 형식이 올바른지 보장 할 수 없기 때문에 와일드 카드 인수를 실제로 만족시킬 수 없습니다. add가 일반적인 이유는 컬렉션에 형식을 추가하기 전에 형식이 올바른지 확인해야하기 때문입니다. 그러나 객체를 제거 할 때 유형이 올바르지 않으면 아무 것도 일치하지 않습니다.

아마 잘 설명하지 않았지만 그것은 나에게 논리적으로 보입니다.


답변

다른 답변 외에도 메소드 Object가 술어를 허용 해야하는 또 다른 이유 가 있습니다. 다음 샘플을 고려하십시오.

class Person {
    public String name;
    // override equals()
}
class Employee extends Person {
    public String company;
    // override equals()
}
class Developer extends Employee {
    public int yearsOfExperience;
    // override equals()
}

class Test {
    public static void main(String[] args) {
        Collection<? extends Person> people = new ArrayList<Employee>();
        // ...

        // to remove the first employee with a specific name:
        people.remove(new Person(someName1));

        // to remove the first developer that matches some criteria:
        people.remove(new Developer(someName2, someCompany, 10));

        // to remove the first employee who is either
        // a developer or an employee of someCompany:
        people.remove(new Object() {
            public boolean equals(Object employee) {
                return employee instanceof Developer
                    || ((Employee) employee).company.equals(someCompany);
        }});
    }
}

요점은 remove메소드 에 전달되는 객체가 메소드 정의를 담당한다는 equals것입니다. 이렇게하면 술어 작성이 매우 간단 해집니다.


답변

하나의 모음이 가정 Cat및 유형의 일부 객체 참조 Animal, Cat, SiameseCat,와 Dog. 컬렉션에 참조 Cat또는 SiameseCat참조로 참조 된 개체가 포함되어 있는지 묻는 것이 합리적입니다. 참조에 의해 Animal참조 된 객체가 포함되어 있는지 묻는 것은 어리석은 것처럼 보일 수 있지만 여전히 완벽하게 합리적입니다. 문제의 객체는 결국이며 Cat컬렉션에 나타날 수 있습니다.

또한 객체가가 아닌 다른 객체 일지라도 Cat컬렉션에 표시되는지 여부를 말하는 데 아무런 문제가 없습니다. 간단히 “아니요, 그렇지 않습니다”라고 대답하십시오. 어떤 유형의 “조회 스타일”컬렉션은 의미있는 수퍼 타입의 참조를 수용하고 개체가 컬렉션 내에 존재하는지 여부를 결정할 수 있어야합니다. 전달 된 객체 참조가 관련이없는 유형 인 경우 컬렉션에이를 포함 할 수있는 방법이 없으므로 쿼리는 의미가 없습니다 (항상 “아니오”로 응답 함). 그럼에도 불구하고 매개 변수를 하위 유형 또는 수퍼 유형으로 제한하는 방법은 없으므로 컬렉션의 유형과 관련이없는 개체에 대해 모든 유형을 수락하고 “아니오”라고 대답하는 것이 가장 실용적입니다.


답변

필자는 항상 remove ()가 어떤 유형의 객체를 제공 할 지에 대한 이유가 없기 때문에 이것을 알았습니다. 어떤 객체에서든 equals ()를 호출 할 수 있기 때문에 해당 객체가 Collection에 포함 된 객체 중 하나인지 확인하는 것은 쉽습니다. add ()에서 유형을 확인하여 해당 유형의 객체 만 포함하는지 확인해야합니다.


답변

타협이었습니다. 두 방법 모두 장점이 있습니다.

  • remove(Object o)
    • 더 유연합니다. 예를 들어 숫자 목록을 반복하여 long 목록에서 제거 할 수 있습니다.
    • 이 유연성을 사용하는 코드를보다 쉽게 ​​생성 할 수 있습니다
  • remove(E e) 반바지 목록에서 정수를 실수로 제거하는 것과 같이 컴파일 타임에 미묘한 버그를 감지하여 대부분의 프로그램이 원하는 것에 더 많은 유형 안전을 제공합니다.

이전 버전과의 호환성은 Java API를 발전시킬 때 항상 주요 목표 였으므로 기존 코드를보다 쉽게 ​​생성 할 수 있도록 remove (Object o)를 선택했습니다. 이전 버전과의 호환성에 문제가 없다면 디자이너가 remove (E e)를 선택했을 것입니다.