Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenJDK 1.6 Bug while doing nested type inference?

I have the following sample Java program which compiles fine under a Oracle JDK but not on 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> {}

Compiling it with

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)

and

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)

works fine, but fails with

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)

I get the following compile error here:

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

So my question is, if this is a bug in OpenJDK or whether I'm doing something wrong here with generics' type inference ?


Since in the comments below, there are some question about the usefulness of this code (albeit the question was purely syntax related ;-), here is the given examples with some more meat. It uses a double-dispatch to let the request create the response itself and uses a parameterized execute to make the usage as typesafe as possible to allow only the same pairs of request and response types. The same scenario could probably done as well with multiple, overloaded execute() methods, too. Regardless, whether this code is useful or not, the question about the different syntactic handling between Oracle JDK and OpenJDK 1.6 for this use case remains.

 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;
    }
}
like image 274
Roland Huß Avatar asked Dec 12 '25 20:12

Roland Huß


1 Answers

imagine you have also

class AResp2 extends Response<AReq> {}

then both senetences are equally (il)legal:

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

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

So I believe OpenJDK is correct here.

What can be done to fix the problem:

public Response execute(T request) { }

and then manually cast Response to AResp.

Or tightly connect request to response, so that compiler could unambiguously determine response type by request type:

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) {  }
like image 71
Alexei Kaigorodov Avatar answered Dec 14 '25 10:12

Alexei Kaigorodov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!