我有以下示例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来尽可能使用类型,以便只允许相同的请求和响应类型对。使用多个重载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;
}
}
答案 0 :(得分:1)
想象你也
class AResp2 extends Response<AReq> {}
AResp resp = bug.execute(new AReq());
AResp2 resp = bug.execute(new AReq());
所以我相信OpenJDK在这里是正确的。
可以采取哪些措施来解决问题:
公共响应执行(T请求){}
然后手动将响应转换为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) { }
答案 1 :(得分:1)
你的问题是execute
太泛型,正如阿列克谢的答案所示。你怎么能实现一个方法,可以为任何R
返回Response< T >
的任何所需子类T
(尽管return null
在技术上应该有效,这就是为什么我认为Oracle JDK实际上是正确的。)
GenericsBug
该课程,而非GenericsBug.execute
方法,应以T
和R
作为参数。