我遇到了一些高级的Java代码(对我的高级代码),我需要帮助理解。
在类中有一个嵌套类,如下:
private final class CoverageCRUDaoCallable implements
Callable<List<ClientCoverageCRU>>
{
private final long oid;
private final long sourceContextId;
private CoverageCRUDaoCallable(long oid, long sourceContextId)
{
this.oid = oid;
this.sourceContextId = sourceContextId;
}
@Override
public List<ClientCoverageCRU> call() throws Exception
{
return coverageCRUDao.getCoverageCRUData(oid, sourceContextId);
}
}
后来在外部类中,创建了可呼叫类的实例。我不知道这是什么:
ConnectionHelper.<List<ClientCoverageCRU>> tryExecute(coverageCRUDaoCallable);
对我来说,它看起来不像Java语法。您能详细说明这种神秘的语法中发生了什么吗?您可以在代码摘录中看到下面使用它。
CoverageCRUDaoCallable coverageCRUDaoCallable = new CoverageCRUDaoCallable(
dalClient.getOid(), sourceContextId);
// use Connection helper to make coverageCRUDao call.
List<ClientCoverageCRU> coverageCRUList = ConnectionHelper
.<List<ClientCoverageCRU>> tryExecute(coverageCRUDaoCallable);
编辑添加了Connection Helper类。
public class ConnectionHelper<T>
{
private static final Logger logger =
LoggerFactory.getLogger(ConnectionHelper.class);
private static final int CONNECTION_RETRIES = 3;
private static final int MIN_TIMEOUT = 100;
public static <T> T tryExecute(Callable<T> command)
{
T returnValue = null;
long delay = 0;
for (int retry = 0; retry < CONNECTION_RETRIES; retry++)
{
try
{
// Sleep before retry
Thread.sleep(delay);
if (retry != 0)
{
logger.info("Connection retry #"+ retry);
}
// make the actual connection call
returnValue = command.call();
break;
}
catch (Exception e)
{
Throwable cause = e.getCause();
if (retry == CONNECTION_RETRIES - 1)
{
logger.info("Connection retries have exhausted. Not trying "
+ "to connect any more.");
throw new RuntimeException(cause);
}
// Delay increased exponentially with every retry.
delay = (long) (MIN_TIMEOUT * Math.pow(2, retry));
String origCause = ExceptionUtils.getRootCauseMessage(e);
logger.info("Connection retry #" + (retry + 1)
+ " scheduled in " + delay + " msec due to "
+ origCause);
+ origCause);
}
}
return returnValue;
}
您经常将类视为通用类,但是方法也可以是通用的。一个常见的例子是Arrays.asList
。
大多数情况下,即使您调用了通用方法,您也不必使用具有角度括号的语法<...>
,因为这是Java编译器实际上能够进行基本类型的地方在某些情况下推断。例如,Arrays.asList
文档中给出的片段省略了类型:
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
,但这相当于明确给出通用类型的此版本:
List<String> stooges = Arrays.<String>asList("Larry", "Moe", "Curly");
这是因为,在Java 7之前,仿制药不能完全支持目标键入,因此您需要用所谓的类型证人来帮助编译器。ConnectionHelper.<List<ClientCoverageCRU>>
。
但是,请注意,Java 8显着改善了目标键入,在您的特定示例中,Java 8中不需要类型的证人。
这很丑陋,但是有效的。
无论 ConnectionHelper
是什么,它都有一种静态的tryExecute
方法,需要推断通用类型。
类似:
public static <T> T tryExecute() { ... }
从更新的问题中编辑: Java具有通用类型的类型推理。方法签名中的第一个<T>
表示该方法被调用时将被推断。
在您更新的帖子中,您显示的tryExecute()
定义为通用参数:
public static <T> T tryExecute(Callable<T> command)
这实际上意味着使用该语法是完全多余的,不必要的。T
(类型)是根据必须实现Callable<T>
的command
推断的。该方法被定义为返回推断的类型T
的东西。
Infer a type
|
v
public static <T> T tryExecute(Callable<T> command)
^ ^
| |
<-return type--------------------------
在您的示例中,coverageCRUDaoCallable
必须实现Callable<List<ClientCoverageCRU>>
,因为该方法正在返回List<ClientCoverageCRU>
在上面的示例中,您使用您要问的语法,因为没有任何传递可以推断出类型的语法。T
必须通过使用ConnectionHelper.<List<ClientCoverageCRU>>tryExecute()
从 Java Generics and Collections
,
List<Integer> ints = Lists.<Integer>toList(); // first example
List<Object> objs = Lists.<Object>toList(1, "two"); // second example
-
In the first example
,没有类型参数,信息太少了Sun的编译器使用的推理算法类型来推断正确的类型。它不断增加tolist的论点是任意通用类型的空数阵列,而不是一个空的整数,这触发了前面描述的未检查的警告。(Eclipse编译器使用不同的推理算法,并编译相同的行正确没有明确的参数。) -
In the second example
,没有类型参数类型推理算法的信息以推断正确类型。您可能会认为该对象是整数唯一的类型字符串有共同点,但实际上它们都实现了接口可序列化且可比较。类型推理算法无法选择这三种是正确的类型。
通常,以下经验法则就足够了:
在对通用方法的调用中,如果有是一个或多个与类型参数相对应的参数,它们都有相同的类型然后可以推断类型参数;如果没有争论对应于类型参数或参数属于该类型的不同子类型预期类型然后必须明确给出类型参数。
传递类型参数的一些分
当类型参数传递给通用方法调用时,它以角度出现就像在方法声明中一样,左侧的括号。
Java语法要求该类型参数只能出现在使用虚线形式的方法调用中。甚至如果在调用代码的同一类中定义了方法的宽容仪,我们将无法缩短如下:
List<Integer> ints = <Integer>toList(); // compile-time error
这是非法的,因为它会混淆解析器。
基本上, ConnectionHelper
中的 tryExecute()
方法使用grendics。这使您可以在"点运算符"之后进行方法调用之前将类型推理提供给它。实际上,这直接在Oracle Java教程中显示了仿制药的教程,即使我认为这是在生产环境中的不良练习。
您可以在这里看到一个官方示例。
您在修改后的帖子中看到,tryExecute()
定义是:
public static <T> T tryExecute(Callable<T> command)
通过这样称呼(<List<ClientCoverageCRU>> tryExcute
),您将T
成为List<ClientCoverageCRU>
。不过,总的来说,一种更好的做法是让这是从该方法中的实际论证中推断出来的。该类型也可以从Callable<T>
推断出来,因此将其提供为Callable<List<ClientCoverageCRU>>
作为参数将消除这种混乱的用法的需求。
在JLS 4.11中查看其用法 - 使用类型:
<S> void loop(S s) { this.<S>loop(s); }
...以及JLS 15.12中的方法调用中允许这样做的形式定义 - 方法调用表达式。您可以跳至15.12.2.7和15.12.2.8,以获取更多细节。15.12.2.8-推断未解决类型的参数解释了这种功能的形式逻辑。