如何使用JAVA泛型避免代码冗余

huangapple go评论138阅读模式
英文:

How to avoid code redundancy with JAVA generics

问题

以下是您要翻译的内容:

我有两个类似的函数,它们向不同的API发送不同的请求:

// 填充请求
CompletableFuture<ReqType1> future = new CompletableFuture<>();
RpcClientController rpcClientController = new RpcClientController();
service1.api1(rpcClientController, request1, future::complete);
RspType1 response;
try {
    if (rpcClientController.isFailed()) {
        log.error();
        return null;
    }
    response = future.get(5, TimeUnit.SECONDS);
    if (response == null) {
        log.error();
        return null;
    }
} catch (InterruptedException e) {
    log.error("error: {}", e.getMessage());
    return null;
} catch (ExecutionException e) {
    log.error("error: {}", e.getMessage());
    return null;
} catch (TimeoutException e) {
    log.error("error: {}", e.getMessage());
    return null;
}
// 处理响应

另一个:

// 填充请求
CompletableFuture<ReqType2> future = new CompletableFuture<>();
RpcClientController rpcClientController = new RpcClientController();
service2.api2(rpcClientController, request2, future::complete);
RspType2 response;
try {
    if (rpcClientController.isFailed()) {
        log.error();
        return null;
    }
    response = future.get(5, TimeUnit.SECONDS);
    if (response == null) {
        log.error();
        return null;
    }
} catch (InterruptedException e) {
    log.error("error: {}", e.getMessage());
    return null;
} catch (ExecutionException e) {
    log.error("error: {}", e.getMessage());
    return null;
} catch (TimeoutException e) {
    log.error("error: {}", e.getMessage());
    return null;
}
// 处理响应

在C++中,可以使用一个template方法来处理这种情况。我认为C++编译器会分别为<ReqType1,RspType1,SrvType1,ApiFunc1>和<ReqType2,RspType2,SrvType2,ApiFunc2>创建两种方法。

然而,在Java中,无法正确编译以下代码:

protected <Request, Service, Response> 
Response callService(Request request, Service service, Response response) {
    ...
    service.api1(rpcClientController, request, future::complete); 
    // 错误,无法解析方法 'api1' 在 'service' 中
}

看起来泛型类型Service对于Java编译器来说与Object相同。那么通常泛型方法是什么样的呢?

英文:

I have two similar functions which send different requests to different APIs:

// fill request
CompletableFuture&lt;ReqType1&gt; future = new CompletableFuture&lt;&gt;();
RpcClientController rpcClientController = new RpcClientController();
service1.api1(rpcClientController, request1, future::complete);
RspType1 response;
try {
    if (rpcClientController.isFailed()) {
        log.error();
        return null;
    }
    response = future.get(5, TimeUnit.SECONDS);
    if (response == null) {
        log.error();
        return null;
    }
} catch (InterruptedException e) {
    log.error(&quot;error: {}&quot;, e.getMessage());
    return null;
} catch (ExecutionException e) {
    log.error(&quot;error: {}&quot;, e.getMessage());
    return null;
} catch (TimeoutException e) {
    log.error(&quot;error: {}&quot;, e.getMessage());
    return null;
}
// process response

The other:

// fill request
CompletableFuture&lt;ReqType2&gt; future = new CompletableFuture&lt;&gt;();
RpcClientController rpcClientController = new RpcClientController();
service2.api2(rpcClientController, request2, future::complete);
RspType2 response;
try {
    if (rpcClientController.isFailed()) {
        log.error();
        return null;
    }
    response = future.get(5, TimeUnit.SECONDS);
    if (response == null) {
        log.error();
        return null;
    }
} catch (InterruptedException e) {
    log.error(&quot;error: {}&quot;, e.getMessage());
    return null;
} catch (ExecutionException e) {
    log.error(&quot;error: {}&quot;, e.getMessage());
    return null;
} catch (TimeoutException e) {
    log.error(&quot;error: {}&quot;, e.getMessage());
    return null;
}
// process response

A template method in C++ can handle this. I think C++ compiler creates two methods for <ReqType1, RspType1, SrvType1, ApiFunc1> and <ReqType2, RspType2, SrvType2, ApiFunc2>, respectively.

Nevertheless, this cannot be compiled properly in Java:

protected &lt;Request, Service, Response&gt; 
Response callService(Request request, Service service, Response response) {
    ...
    service.api1(rpcClientController, request, future::complete); 
    // wrong, cannot resolve method &#39;api1&#39; in &#39;service&#39;
}

It seems that generic type Service is the same as Object to Java compiler. So what does a generic method usually look like?

答案1

得分: 2

你的实现已经很接近了,但你声明类型参数的方式是不正确的。应该更像这样:

protected <T extends Request, S extends Service, R extends Response>
Response callService(T request, S service, R response) {
    ...
    service.api1(rpcClientController, request, future::complete); 
}

按照惯例,在 Java 中,类型参数通常是一个大写字母。为了让它们按照你的期望行为工作,你需要限定类型;因此使用了 extends

英文:

You're pretty close with this, the way you've declared your type parameters is incorrect though. It should be more like:

protected &lt;T extends Request, S extends Service, R extends Response&gt; 
Response callService(T request, S service, R response) {
    ...
    service.api1(rpcClientController, request, future::complete); 
}

By convention in Java type parameters are a single uppercase letter. To get them to behave as you expect you need to bound the types; hence the extends.

huangapple
  • 本文由 发表于 2020年7月23日 12:01:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63046703.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定