web-dev-qa-db-ja.com

メソッドは型の別のメソッドと同じ消去をします

これら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つの名前を変更することはできません。

336
Omry Yadan

この規則は、まだ生の型を使用している従来のコードでの競合を避けることを目的としています。

これが許可されなかった理由の説明は次のとおりです。 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>)メソッドをオーバーライドするのに有効な形式です。しかし、もちろん、コンパイラは単一のメソッドを解決する必要があります。このあいまいさを排除するために、クラスには、オーバーライドに相当する複数のメソッド、つまり消去後に同じパラメータ型を持つ複数のメソッドを含めることはできません。

重要なのは、これが生の型を使って古いコードとの互換性を保つように設計された言語規則であるということです。型パラメータの消去に必要な制限ではありません。メソッドの解決はコンパイル時に行われるため、メソッド識別子にジェネリック型を追加すれば十分です。

325
erickson

Javaジェネリックスは型消去を使用します。山括弧内のビット(<Integer><String>)は削除されるので、同じシグネチャを持つ2つのメソッド(エラーに表示されるadd(Set))になります。ランタイムは、それぞれの場合にどちらを使用すればよいかわからないため、これは許可されません。

Javaが具体化された総称を手に入れたことがあるなら、あなたはこれをすることができます、しかしそれはおそらく今はありそうもないです。

103
GaryF

これは、Java Genericsが Type Erasure で実装されているためです。

あなたのメソッドはコンパイル時に次のように変換されます。

メソッドの解決はコンパイル時に行われ、型パラメータは考慮されません。 ( ericksonの回答を参照

void add(Set ii);
void add(Set ss);

どちらのメソッドもtypeパラメータなしで同じシグネチャを持つため、エラーになります。

41
bruno conde

問題は、Set<Integer>Set<String>が実際にはJVMからのSetとして扱われることです。 Setの型(あなたの場合はStringまたはInteger)を選択することは、コンパイラによって使用される構文上の糖分のみです。 JVMはSet<String>Set<Integer>を区別できません。

19
kgiannakakis

void add(Set ii){}のように型なしで単一のMethodを定義する

あなたの選択に基づいてメソッドを呼び出しながら、あなたはタイプを言及することができます。それはどんな種類のセットのためにも働くでしょう。

5
Idrees Ashraf

JavaバイトコードでコンパイラがSet(Integer)をSet(Object)に変換する可能性があります。この場合、Set(Integer)は構文チェックのためにコンパイル段階でのみ使用されます。

3
rossoft