我在 Effective 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
(理论上可能是不同类型的请求)传递给克隆方法,这是一个奇怪的问题。
我可以看到两种解决方案,具体取决于您是否真的需要 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;
}
...
}
上述解决方案的缺点是 cloneRequest()
的返回值对于 GET 请求和 POST 请求是相同的。
如果您愿意,可以通过向枚举中添加代码来删除开关:
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);
}
...
}
如果您希望返回值取决于请求的类型,那么调用者将需要指定某种类型的标记,显式引用 RequestCloner
的子类,或者添加一个静态方法RequestCloner
用于每种类型的请求。
如果 RequestCloner 需要通用
鉴于问题中的代码,使 RequestCloner
通用的唯一好处是使 clone()
的返回值对于 GET 或 POST 不同。
为此,您有两个选择
- 公开子类(及其构造函数)。
- 用多个创建方法替换您的
newInstance()
方法
这是选项 2 的示例:
public static RequestCloner<HttpPost> forPostRequest(String URL) {
return new PostRequestCloner(URL);
}
关于java - 静态工厂模式返回类型中的有界通配符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31646828/