Programming

Java Collections가 일반적인 메소드를 제거하지 않는 이유는 무엇입니까?

procodes 2020. 6. 21. 21:17
반응형

Java Collections가 일반적인 메소드를 제거하지 않는 이유는 무엇입니까?


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것입니다. 이렇게하면 술어 작성이 매우 간단 해집니다.


Assume one has a collection of Cat, and some object references of types Animal, Cat, SiameseCat, and Dog. Asking the collection whether it contains the object referred to by the Cat or SiameseCat reference seems reasonable. Asking whether it contains the object referred to by the Animal reference may seem dodgy, but it's still perfectly reasonable. The object in question might, after all, be a Cat, and might appear in the collection.

Further, even if the object happens to be something other than a Cat, there's no problem saying whether it appears in the collection--simply answer "no, it doesn't". A "lookup-style" collection of some type should be able to meaningfully accept reference of any supertype and determine whether the object exists within the collection. If the passed-in object reference is of an unrelated type, there's no way the collection could possibly contain it, so the query is in some sense not meaningful (it will always answer "no"). Nonetheless, since there isn't any way to restrict parameters to being subtypes or supertypes, it's most practical to simply accept any type and answer "no" for any objects whose type is unrelated to that of the collection.


I always figured this was because remove() has no reason to care what type of object you give it. It's easy enough, regardless, to check if that object is one of the ones the Collection contains, since it can call equals() on anything. It's necessary to check type on add() to ensure that it only contains objects of that type.


It was a compromise. Both approaches have their advantage:

  • remove(Object o)
    • is more flexible. For example it allows to iterate through a list of numbers and remove them from a list of longs.
    • code that uses this flexibility can be more easily generified
  • remove(E e) brings more type safety to what most programs want to do by detecting subtle bugs at compile time, like mistakenly trying to remove an integer from a list of shorts.

Backwards compatibility was always a major goal when evolving the Java API, therefore remove(Object o) was chosen because it made generifying existing code easier. If backwards compatibility had NOT been an issue, I'm guessing the designers would have chosen remove(E e).


Remove is not a generic method so that existing code using a non-generic collection will still compile and still have the same behavior.

See http://www.ibm.com/developerworks/java/library/j-jtp01255.html for details.

Edit: A commenter asks why the add method is generic. [...removed my explanation...] Second commenter answered the question from firebird84 much better than me.


Another reason is because of interfaces. Here is an example to show it :

public interface A {}

public interface B {}

public class MyClass implements A, B {}

public static void main(String[] args) {
   Collection<A> collection = new ArrayList<>();
   MyClass item = new MyClass();
   collection.add(item);  // works fine
   B b = item; // valid
   collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}

Because it would break existing (pre-Java5) code. e.g.,

Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);

Now you might say the above code is wrong, but suppose that o came from a heterogeneous set of objects (i.e., it contained strings, number, objects, etc.). You want to remove all the matches, which was legal because remove would just ignore the non-strings because they were non-equal. But if you make it remove(String o), that no longer works.

참고URL : https://stackoverflow.com/questions/104799/why-arent-java-collections-remove-methods-generic

반응형