Collection.remove(Object o)がジェネリック ではないのはなぜですか?
Collection<E>
できたようですboolean remove(E o);
Set<String>
次に、個々の文字列を から削除する代わりに(たとえば) 誤って削除しようとするCollection<String>
と、後でデバッグの問題ではなく、コンパイル時のエラーになります。
Collection.remove(Object o)がジェネリック ではないのはなぜですか?
Collection<E>
できたようですboolean remove(E o);
Set<String>
次に、個々の文字列を から削除する代わりに(たとえば) 誤って削除しようとするCollection<String>
と、後でデバッグの問題ではなく、コンパイル時のエラーになります。
Josh Bloch と Bill Pugh は、Java Puzzlers IV: The Phantom Reference Menace、Attack of the Clone、および Revenge of The Shift でこの問題について言及しています。
Josh Bloch は (6:41)、Map の get メソッド、remove メソッドなどを生成しようとしたが、「うまくいかなかった」と述べています。
コレクションのジェネリック型のみをパラメーター型として許可すると、ジェネリック化できない合理的なプログラムが多すぎます。List
彼が示した例は、s の aとNumber
s の
aList
の交差ですLong
。
型パラメーターがワイルドカードの場合、一般的な 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()にどのタイプのオブジェクトを指定するかを気にする理由がないためだといつも思っていました。とにかく、そのオブジェクトがコレクションに含まれているオブジェクトの1つであるかどうかを確認するのは簡単です。これは、任意のオブジェクトに対してequals()を呼び出すことができるためです。add()でタイプをチェックして、そのタイプのオブジェクトのみが含まれていることを確認する必要があります。
それは妥協でした。どちらのアプローチにも利点があります。
remove(Object o)
remove(E e)
short のリストから誤って整数を削除しようとするなど、コンパイル時に微妙なバグを検出することで、ほとんどのプログラムが実行したいことにより多くの型の安全性をもたらします。下位互換性は、Java API を進化させる際の主要な目標でした。したがって、remove(Object o) が選択されたのは、既存のコードの生成が容易になったためです。下位互換性が問題にならなかった場合、設計者は remove(E e) を選択していたと思います。
Removeはジェネリックメソッドではないため、非ジェネリックコレクションを使用する既存のコードは引き続きコンパイルされ、同じ動作をします。
詳細については、 http://www.ibm.com/developerworks/java/library/j-jtp01255.htmlを参照してください。
編集:コメント投稿者は、addメソッドがジェネリックである理由を尋ねます。[...私の説明を削除しました...]2番目のコメント投稿者はfirebird84からの質問に私よりもはるかによく答えました。
もう 1 つの理由は、インターフェイスが原因です。これを示す例を次に示します。
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 */
}
既存の(Java5より前の)コードを壊してしまうからです。例えば、
Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);
上記のコードは間違っていると言うかもしれませんが、o が異質なオブジェクトのセット (つまり、文字列、数値、オブジェクトなどを含んでいた) に由来すると仮定してください。すべての一致を削除する必要があります。これは、remove が非文字列を無視するだけであるため、正当でした。それらは等しくないためです。しかし、remove(String o) にすると、機能しなくなります。