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;
}
}
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) { }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With