我在《有效的Java》中读到,您不应该将有界通配符用作返回类型,但是我不知道该怎么做。我的代码编译的唯一方法是在静态工厂中使用RequestCloner<? extends HttpUriRequest>
作为返回类型。我是在做错什么还是有解决方法?
注意:需要注意的一件事是HttpUriRequest
具有方法setHeader
,但是只有HttpPost
具有方法setEntity
。
abstract class RequestCloner<T extends HttpUriRequest> {
protected T clonedRequest;
private enum RequestType {
GET, POST, DELETE
}
static RequestCloner<? extends HttpUriRequest> newInstance(
String type, String url) {
RequestType requestType = RequestType.valueOf(type);
switch (requestType) {
case GET:
return new GetRequestCloner(url);
case POST:
return new PostRequestCloner(url);
case DELETE:
return new DeleteRequestCloner(url);
default:
throw new IllegalArgumentException(String.format(
"Method '%s' not supported",
type));
}
}
public abstract HttpUriRequest clone(HttpServletRequest servletRequest) throws IOException;
protected void cloneHeaders(HttpServletRequest servletRequest) {
@SuppressWarnings("unchecked")
Enumeration<String> e = servletRequest.getHeaderNames();
while (e.hasMoreElements()) {
String header = e.nextElement();
if (!header.equalsIgnoreCase("Content-Length")
&& !header.equalsIgnoreCase("Authorization")
&& !header.equalsIgnoreCase("Host")) {
clonedRequest.setHeader(new BasicHeader(header, servletRequest.getHeader(header)));
}
}
}
}
...
class GetRequestCloner extends RequestCloner<HttpGet> {
GetRequestCloner(String url) {
this.clonedRequest = new HttpGet(url);
}
@Override
public HttpUriRequest clone(HttpServletRequest servletRequest) {
cloneHeaders(servletRequest);
return clonedRequest;
}
}
...
class PostRequestCloner extends RequestCloner<HttpPost> {
private static final int MAX_STR_LEN = 1024;
PostRequestCloner(String url) {
this.clonedRequest = new HttpPost(url);
}
@Override
public HttpUriRequest clone(HttpServletRequest servletRequest) throws IOException {
cloneHeaders(servletRequest);
cloneBody(servletRequest);
return clonedRequest;
}
private void cloneBody(HttpServletRequest servletRequest) throws IOException {
StringBuilder sb = new StringBuilder("");
BufferedReader br = new BufferedReader(new InputStreamReader(
servletRequest.getInputStream(),
"UTF-8"));
String line = "";
while ((line = br.readLine()) != null && sb.length() < MAX_STR_LEN) {
sb.append(line);
}
br.close();
clonedRequest.setEntity(new StringEntity(sb.toString(), "UTF-8"));
}
}
...
class DeleteRequestCloner extends RequestCloner<HttpDelete> {
DeleteRequestCloner(String url) {
this.clonedRequest = new HttpDelete(url);
}
@Override
public HttpUriRequest clone(HttpServletRequest servletRequest) {
cloneHeaders(servletRequest);
return clonedRequest;
}
}
最佳答案
查看您的代码,您的类不需要是通用的。进一步看,还有一个奇怪的问题,即调用者传递URL来创建克隆器,然后将HttpServletRequest
(理论上可以是不同类型的请求)传递给clone方法。
我可以看到两种解决方案,具体取决于您是否真的需要RequestCloner
才能通用。
如果RequestCloner不需要是通用的
更改基类,如下所示:
abstract class RequestCloner {
private enum RequestType {
GET, POST, DELETE
}
public static HttpUriRequest cloneRequest(HttpServletRequest servletRequest)
throws IOException {
RequestCloner cloner = createCloner(servletRequest);
String uri = servletRequest.getRequestURI();
return cloner.clone(uri, servletRequest);
}
private static RequestCloner createCloner(HttpServletRequest servletRequest) {
RequestType requestType = RequestType.valueOf(servletRequest. getMethod());
switch (requestType) {
case GET:
return new GetRequestCloner();
case POST:
return new PostRequestCloner();
case DELETE:
return new DeleteRequestCloner();
default:
throw new AssertionFailedError(String.format(
"RequestType '%s' not supported", requestType));
}
}
protected abstract HttpUriRequest clone(
String uri, HttpServletRequest servletRequest)
throws IOException;
protected final void cloneHeaders(
HttpServletRequest servletRequest,
HttpUriRequest clonedRequest) { // note addition of parameter
// same code as before, but modify the passed-in clonedRequest
}
}
RequestCloner
的子类将覆盖clone()
,可以选择更改返回值以返回HttpUriRequest
的子类:class PostRequestCloner extends RequestCloner {
private static final int MAX_STR_LEN = 1024;
@Override
protected HttpPost clone(
String uri, HttpServletRequest servletRequest)
throws IOException {
HttpPost clonedRequest = new HttpPost(uri);
cloneHeaders(servletRequest, clonedRequest);
cloneBody(servletRequest, clonedRequest);
return clonedRequest;
}
...
}
上述解决方案的缺点是,GET请求的POST请求的
cloneRequest()
的返回值是相同的。如果愿意,可以通过将代码添加到枚举来删除该开关:
abstract class RequestCloner {
private enum RequestType {
GET(new GetRequestCloner()),
POST(new PostRequestCloner()),
DELETE(new DeleteRequestCLoner());
private final RequestCloner requestCloner;
private RequestType(RequestCloner requestCloner) {
this.requestCloner = requestCloner();
}
}
public static HttpUriRequest cloneRequest(HttpServletRequest servletRequest)
throws IOException {
RequestType requestType = RequestType.valueOf(servletRequest. getMethod());
String uri = servletRequest.getRequestURI();
return requestType.requestCloner.clone(uri, servletRequest);
}
...
}
如果希望返回值取决于请求的类型,则调用者将需要指定某种类型的 token ,显式引用
RequestCloner
的子类,或为每种请求的类型向RequestCloner
添加一个静态方法。如果RequestCloner需要通用
给定问题中的代码,使
RequestCloner
通用的唯一好处是对于GET或POST,使clone()
的返回值不同。为此,您有两种选择
newInstance()
方法这是选项2的示例:
public static RequestCloner<HttpPost> forPostRequest(String URL) {
return new PostRequestCloner(URL);
}