3

次のサンプル Java プログラムは、Oracle JDK では正常にコンパイルされますが、OpenJDK ではコンパイルされません。

public class GenericsBug {

  public static void main(String[] args) {
      GenericsBug bug = new GenericsBug();
      // This line causes the error for not finding matching types:
      AResp resp = bug.execute(new AReq());
  }

  public <T extends Request,R extends Response<T>> R execute(T request) {
      return null;
  }
}

class Request { }
class Response<T extends Request> {}

class AReq extends Request {}
class AResp extends Response<AReq> {}

でコンパイルする

java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04-415-11M3635)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01-415, mixed mode)

java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.1pre) (7~u3-2.1.1~pre1-1ubuntu3)
OpenJDK Client VM (build 22.0-b10, mixed mode, sharing)

正常に動作しますが、失敗します

java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.13) (6b18-1.8.13-0+squeeze1)
OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)

ここで次のコンパイル エラーが発生します。

GenericsBug.java:9: type parameters of <R>R cannot be determined; no unique maximal instance exists for type variable R with upper bounds AResp,Response<T>
    AResp resp = bug.execute(new AReq());
                            ^
 1 error

だから私の質問は、これが OpenJDK のバグなのか、それともここでジェネリックの型推論で何か間違ったことをしているのかということです。


以下のコメントでは、このコードの有用性についていくつかの質問があるため (質問は純粋に構文に関連していましたが ;-)、ここにいくつかの具体的な例を示します。ダブルディスパッチを使用してリクエスト自体がレスポンスを作成できるようにし、パラメーター化された実行を使用して可能な限りタイプセーフな使用法を作成し、リクエストとレスポンス タイプの同じペアのみを許可します。execute()同じシナリオは、複数のオーバーロードされたメソッドでも同様に機能する可能性があります。このコードが有用であるかどうかに関係なく、このユース ケースでの Oracle JDK と OpenJDK 1.6 の間の異なる構文処理に関する疑問は残ります。

 public class Client {

   public static void main(String[] args) {
     Client client = new Client();

     // AReq and AResp must match, otherwise an compile error will happen
     AResp respA = client.execute(new AReq());
     System.out.println(respA.getAContent());

     // Same for BReq and BResp
     BResp respB = client.execute(new BReq());
     System.out.println(respB.getBContent());
   }

   public <T extends Request,R extends Response<T>> R execute(T request) {
     // Fetch the response somehow, eg. by using a HttpClient:
     String responseBody = "....";
     // Let the request itself create the response
     return request.createResponse(responseBody);
 }

}
// ==================================================================================

// Abstract definition of a requests
abstract class Request {
    abstract <R extends Response<? extends Request>> R createResponse(String content); 
}
class Response<T extends Request> {}

// Two request/response pairs with specific request/response specific members
class AReq extends Request {
    AResp createResponse(String content) {
        return new AResp(content);
    }
}
class AResp extends Response<AReq> {
    private String aContent;

    public AResp(String pContent) {
        aContent = "AResp: " + pContent;
    }

    public String getAContent() {
        return aContent;
    }
} 

class BReq extends Request {
    BResp createResponse(String content) {
        return new BResp(content);
    }
}
class BResp extends Response<BReq> {
    private String bContent;

    public BResp(String pContent) {
        bContent = "BResp: " + pContent;
    }

    public String getBContent() {
        return bContent;
    }
}
4

2 に答える 2

1

あなたも持っていると想像してください

class AResp2 extends Response<AReq> {}

その場合、両方のセネテンスは等しく(非)合法です:

AResp resp = bug.execute(new AReq());

AResp2 resp = bug.execute(new AReq());

ですから、ここではOpenJDKが正しいと思います。

問題を解決するためにできること:

public Response execute(T request){}

次に、手動でResponseをARespにキャストします。

または、リクエストをレスポンスに緊密に接続して、コンパイラがリクエストタイプごとにレスポンスタイプを明確に判断できるようにします。

class Request<R extends Response> { }
class Response {}

class AReq extends Request<AResp> {}
class AResp extends Response {}

public <T extends Request<R>, R extends Response> R execute(T request) {  }
于 2012-06-17T07:43:23.347 に答える
1

アレクセイの答えが示すように、あなたの問題はexecuteあまりにも一般的です。for anyの任意のサブクラスRを返すことができるメソッドをどのように実装できますか(ただし、技術的には機能するはずですが、それが Oracle JDK が実際に正しいと私が考える理由です)。Response< T >Treturn null

GenericsBugGenericsBug.executeメソッドではなく、クラスがパラメーターとしてTandを受け取る必要がありRます。

于 2012-06-17T08:34:17.313 に答える