web-dev-qa-db-ja.com

コンパイラが一意の最大インスタンスが存在しないと述べているのはなぜですか?

私は次のクラスを持っています:

_public class Obj<T> extends BaseModel {

    public static final String OBJECT = "object";

    public Obj(T object) {
        setObject(object);
    }

    public T getObject() {
        return get(OBJECT);
    }

    public void setObject(T object) {
        set(OBJECT, object);
    }
}
_

そして...

_/** This is a 3rd party library class **/
public class BaseModel implements ModelData, Serializable {
  //...members and stuff...

  @SuppressWarnings({"unchecked", "rawtypes"})
  public <X> X get(String property) {
    X obj = null;
    if (start > -1 && end > -1) {
      Object o = map.get(property.substring(0, start));
      String p = property.substring(start + 1, end);
      if (o instanceof Object[]) {
        obj = (X) ((Object[]) o)[Integer.valueOf(p)];
      } else if (o instanceof List) {
        obj = (X) ((List) o).get(Integer.valueOf(p));
      } else if (o instanceof Map) {
        obj = (X) ((Map) o).get(p);
      }
    } else {
      obj = (X) map.get(property);
    }
    return obj;
  }
}
_

コンパイルすると、次のエラーが発生します。

type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,Java.lang.Object -> getObject()

私の知る限り、Antビルドと同じJDKを使用しているEclipseでは発生しません。 Sunコンパイラの問題に関するSOスレッド を見てきましたが、それはその場で型を宣言する静的メソッドのようでした。

なぜこのエラーが発生するのですか、さらに重要なことに、どうすれば回避できますか?

これまでのところ、私が見つけた唯一の理由は、次のように私のメソッドでキャストすることです。

_@SuppressWarnings({"unchecked"})
public T getObject() {
    return (T) get(OBJECT); //yuck
}
_

私がひび割れていると言うと、これは適切な方法です。

26
Snekse

コードがジェネリックに期待しすぎているため、コンパイルされません->つまり、次の<X> X部分:

public <X> X get(String property) { ... }

次のコードでは:

public T getObject() {
  return get(OBJECT);
}

ジェネリックスは常に「展開」されることに注意する必要がありますコンパイラーは実際にJavaコードのコンパイルを開始します。これは前処理ステップです。

あなたの場合、コンパイラはXを置き換えるために何を使用するかを知りませんコンパイル時。コンパイラは、コードを検証するためにXをTと照合する必要があるため、Xのタイプを確認する必要があります。したがって、エラー...

この問題の解決策は、<X> Xをオブジェクトに置き換えることです。

public Object get(String property) { ... }

キャストを追加します:

public T getObject() {
  return (T) get(OBJECT);
}

コンパイル時にチェックされていないキャストの警告が表示されますが、コードはコンパイルされます(したがって、回避策は有効です)。

17

これはダミーです bug Java SE7で修正されました。

21

メソッドタイプのパラメータは、ほとんどの場合、そのメソッドの引数から暗黙的に推測されます。ただし、getには、引数と型パラメーターの間に明示的な関係がないことに注意してください。

public <X> X get(String property)

型推論は通常のパスですが、クラスと同様に、メソッドは明示的な型引数を使用して呼び出すこともできます。形式は宣言の形式にほぼ従っているので、Objの内部では次のことができます。

public T getObject() {
    return super.<T>get(OBJECT);
}

直接直接<Object>を使用することもできますが、それでもTに戻すにはそのチェックされていないキャストを使用する必要があります。明示的な引数には修飾子、通常はクラスのインスタンス名が必要であることに注意してください。あなたの例はスーパークラスのメソッドを使用しているので、その参照はsuperを介して暗黙的に行われます。

これは、非ジェネリッククラス(BaseModel)内にジェネリックメソッド(<X> X get)を適用するという根本的な問題を解決しません。ライブラリ内のコードは、type引数に強制的に型キャストすることに注意してください。このスタイルは確かに、ジェネリック機能を非ジェネリックJavaコードにバックポートするためのソリューションの1つです。ライブラリユーザーからこれを隠そうとしているようですが、そうしなかったためです。 tクラスをジェネリック化して、インスタンスからタイプを推測することはできません(つまり、本当にObj<T> extends BaseModel<T>が必要です)。

[編集:明示的なメソッド型引数を修正して説明しました]

4
Chad Wellington

Apache Pivot を使用しているプロジェクトで同様の問題が発生しました。クライアントコードは次のような行でいっぱいでした:

boolean foo = org.Apache.pivot.json.JSON.get(item, "foo");

コードはEclipseでコンパイルされますが、コマンドラインからMavenまたはjavacを使用しません。 Bug 6302954 のようですが、最新のJDKに更新した後も表示されます。

JSONクラスはPivotによって提供されるため、自分のソースツリー内で変更できるものではありません(ライブラリのフォークはこのプロジェクトのオプションではありません)

私のために働いた解決策は、バグレポートの最初の返信から来て、コードを次のように変更しました。

boolean foo = org.Apache.pivot.json.JSON.<Boolean>get(item, "foo");
1
sworisbreathing