これら2つのメソッドを同じクラスに含めるのは合法的ではないのはなぜですか?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
compilation error
が出ます
メソッドadd(Set)はタイプTestの他のメソッドと同じ消去add(Set)を持ちます。
私はそれを回避することができますが、私はなぜjavacがこれを好まないのか疑問に思いました。
多くの場合、これら2つの方法のロジックは非常に似通っており、単一のものに置き換えることができます。
public void add(Set<?> set){}
しかし、これは必ずしもそうとは限りません。
これらの引数をとる2つのconstructors
が必要な場合、これはさらに面倒です。その場合、constructors
のうちの1つの名前を変更することはできません。
この規則は、まだ生の型を使用している従来のコードでの競合を避けることを目的としています。
これが許可されなかった理由の説明は次のとおりです。 JLSから抜粋 ジェネリックがJavaに導入される前に、次のようなコードを書いたとします。
class CollectionConverter {
List toList(Collection c) {...}
}
あなたは私のクラスをこのように拡張します:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
ジェネリック医薬品の導入後、私は自分のライブラリを更新することにしました。
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
更新する準備ができていないので、Overrider
クラスをそのままにします。 toList()
メソッドを正しくオーバーライドするために、言語設計者は生の型は任意の一般化された型と「オーバーライド同等」であると判断しました。つまり、あなたのメソッドシグネチャは、正式には私のスーパークラスのシグネチャと同じではなくなりますが、それでもあなたのメソッドはオーバーライドされます。
今、時間が経過し、あなたはあなたがあなたのクラスを更新する準備ができていると決心します。しかし、ちょっと手間がかかります。既存の生のtoList()
メソッドを編集する代わりに、のように新しいメソッドを追加します。
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
生の型のオーバーライド等価のため、両方のメソッドはtoList(Collection<T>)
メソッドをオーバーライドするのに有効な形式です。しかし、もちろん、コンパイラは単一のメソッドを解決する必要があります。このあいまいさを排除するために、クラスには、オーバーライドに相当する複数のメソッド、つまり消去後に同じパラメータ型を持つ複数のメソッドを含めることはできません。
重要なのは、これが生の型を使って古いコードとの互換性を保つように設計された言語規則であるということです。型パラメータの消去に必要な制限ではありません。メソッドの解決はコンパイル時に行われるため、メソッド識別子にジェネリック型を追加すれば十分です。
Javaジェネリックスは型消去を使用します。山括弧内のビット(<Integer>
と<String>
)は削除されるので、同じシグネチャを持つ2つのメソッド(エラーに表示されるadd(Set)
)になります。ランタイムは、それぞれの場合にどちらを使用すればよいかわからないため、これは許可されません。
Javaが具体化された総称を手に入れたことがあるなら、あなたはこれをすることができます、しかしそれはおそらく今はありそうもないです。
これは、Java Genericsが Type Erasure で実装されているためです。
あなたのメソッドはコンパイル時に次のように変換されます。
メソッドの解決はコンパイル時に行われ、型パラメータは考慮されません。 ( ericksonの回答を参照 )
void add(Set ii);
void add(Set ss);
どちらのメソッドもtypeパラメータなしで同じシグネチャを持つため、エラーになります。
問題は、Set<Integer>
とSet<String>
が実際にはJVMからのSet
として扱われることです。 Setの型(あなたの場合はStringまたはInteger)を選択することは、コンパイラによって使用される構文上の糖分のみです。 JVMはSet<String>
とSet<Integer>
を区別できません。
void add(Set ii){}
のように型なしで単一のMethodを定義する
あなたの選択に基づいてメソッドを呼び出しながら、あなたはタイプを言及することができます。それはどんな種類のセットのためにも働くでしょう。
JavaバイトコードでコンパイラがSet(Integer)をSet(Object)に変換する可能性があります。この場合、Set(Integer)は構文チェックのためにコンパイル段階でのみ使用されます。