我在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;
}
}
答案 0 :(得分:1)
查看您的代码,您的类不需要是通用的。进一步看,有一个奇怪的问题,调用者传入一个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()
的返回值与作为POST请求的GET请求相同。
如果您愿意,可以通过向枚举添加代码来删除开关:
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
的子类,或者向{{添加一个静态方法1}}用于每种类型的请求。
如果RequestCloner需要通用
考虑到问题中的代码,使RequestCloner
通用的唯一好处是使RequestCloner
的返回值与GET或POST不同。
要做到这一点,你有两个选择
clone()
方法以下是选项2的示例:
newInstance()
答案 1 :(得分:0)
有一种方法可以实现您想要的效果,只需稍加改动:您需要将正确的abstract class RequestCloner<T extends HttpUriRequest> {
protected T clonedRequest;
@SuppressWarnings("unchecked")
static <U extends HttpUriRequest, V extends RequestCloner<U>> V newInstance(
U request, String url) {
Class<U> clazz = request.getClass();
Class<V> clz = null;
if (HttpGet.class == clazz) {
clz = GetRequestCloner.class;
} else if (HttpPost.class == clazz) {
clz = PostRequestCloner.class;
} // etc
try {
return clz.getDeclaredConstructor(String.class).newInstance(url);
} catch (Exception e) {
throw new IllegalArgumentException("Factory error", e);
}
}
// no need for this method to be abstract
public T cloneRequest(HttpServletRequest servletRequest) throws IOException {
cloneHeaders(servletRequest);
return clonedRequest;
}
protected void cloneHeaders(HttpServletRequest servletRequest) {
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);
}
// no need to override cloneRequest here
}
然后,GET如下:
class PostRequestCloner extends RequestCloner<HttpPost> {
PostRequestCloner(String url) {
this.clonedRequest = new HttpPost(url);
}
@Override
public HttpPost cloneRequest(HttpServletRequest servletRequest) throws IOException {
super.cloneRequest(servletRequest);
cloneBody(servletRequest); // Adding POST's body here
return clonedRequest;
}
}
和POST:
GetRequestCloner getCloner = RequestCloner.newInstance(servletRequest, url);
HttpGet get = getCloner.cloneRequest(servletRequest);
PostRequestCloner postCloner = RequestCloner.newInstance(servletRequest, url);
HttpPost post = postCloner.cloneRequest(servletRequest);
用法:
clone()
注意:我已将cloneRequest()
方法名称更改为Clonable.clone()
,以便它不会与Java <html>
<link href='dialog.css' rel='stylesheet' type='text/css'/>
<body>
<div class='dialog' ng-style="{'width': model.width+'px', 'height': model.height+'px', 'top':model.top+'px', 'left':model.left+'px', 'z-index': model.zindex}"
ng-mousedown='zorder()'>
<span class='topbar'>
<button class='minimize' ng-click="minimize()"> _ </button>
</span>
<div class='content'>
<ng-include src=model.template></ng-include>
</div>
<div class='drag'></div> //the resize div
</div>
</body>
</html>
发生冲突。
答案 2 :(得分:-1)
你在找这个吗?
static <V extends HttpUriRequest, K extends RequestCloner<V>> K newInstance((String type, String url)
这里没有在返回类型中使用的通配符,但返回通配符总是不错的主意。
更一般地说,如果你真的希望调用网站能够限制访问该对象,我认为返回带有有界返回类型的内容是完全合理的。
请refer this answer了解详情。