使用点运算符在java中调用奇数方法来访问通用列表

我遇到了一些高级的java代码(对我来说很先进:))我需要帮助理解。

在类中有一个嵌套类,如下所示:

private final class CoverageCRUDaoCallable implements Callable<List> { private final long oid; private final long sourceContextId; private CoverageCRUDaoCallable(long oid, long sourceContextId) { this.oid = oid; this.sourceContextId = sourceContextId; } @Override public List call() throws Exception { return coverageCRUDao.getCoverageCRUData(oid, sourceContextId); } } 

稍后在外部类中,创建了一个可调用类的实例。 我不知道这是什么:

 ConnectionHelper.<List> tryExecute(coverageCRUDaoCallable); 

它对我来说看起来不像java语法。 你能详细说明这种神秘的语法会发生什么吗? 您可以在代码摘录中看到它在下面使用。

 CoverageCRUDaoCallable coverageCRUDaoCallable = new CoverageCRUDaoCallable( dalClient.getOid(), sourceContextId); // use Connection helper to make coverageCRUDao call. List coverageCRUList = ConnectionHelper .<List> tryExecute(coverageCRUDaoCallable); 

EDITED添加了ConnectionHelper类。

 public class ConnectionHelper { 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 tryExecute(Callable 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

大多数情况下,即使在调用generics方法时,也不必使用尖括号<...>的语法,因为这是Java编译器实际上能够执行基本操作的地方在某些情况下进行类型推断。 例如, Arrays.asList文档中给出的代码段省略了类型:

 List stooges = Arrays.asList("Larry", "Moe", "Curly"); 

但它相当于这个版本,其中明确给出了generics类型:

 List stooges = Arrays.asList("Larry", "Moe", "Curly"); 

这是因为,在Java 7之前,generics不完全支持目标类型,因此您需要使用ConnectionHelper.>的类型见证来帮助编译器ConnectionHelper.>

但请注意, Java 8显着改进了目标类型 ,在您的特定示例中,Java 8中不需要类型见证。

这很难看,但有效。

无论ConnectionHelper是什么,它都有一个静态的tryExecute方法,需要推断一个generics类型。

就像是:

 public static  T tryExecute() { ... } 

从更新的问题编辑: Java具有generics类型的类型推断 。 方法签名中的第一个表示在调用方法时将推断类型。

在您更新的post中,您显示了定义的tryExecute()以获取generics参数:

 public static  T tryExecute(Callable command) 

这实际上意味着使用该语法是完全冗余和不必要的; T (类型)是从传递的command推断出来的,其中必须实现Callable 。 该方法被定义为返回推断类型T某些内容。

  Infer a type | v public static  T tryExecute(Callable command) ^ ^ | | <-return type-------------------------- 

在您的示例中, coverageCRUDaoCallable必须实现Callable>因为该方法返回List

在上面的示例中,您必须使用您询问的语法,因为没有传递任何内容来推断类型。 必须通过使用ConnectionHelper.>tryExecute()显式提供T ConnectionHelper.>tryExecute()

基本上, ConnectionHelpertryExecute()方法使用generics。 这允许您在“点运算符”之后的方法调用之前向其提供类型推断。 这实际上直接显示在generics的Oracle Java教程中,尽管我认为它在生产环境中是不好的做法。

你可以在这里看到它的官方例子。

正如您在修改后的post中所看到的, tryExecute()定义是:

 public static  T tryExecute(Callable command) 

通过这样调用它( > tryExcute ),您强制T成为List 。 但是,一般来说,更好的做法是从方法中的实际参数中推断出这一点。 该类型也可以从Callable推断出来,因此为其提供Callable>作为参数将消除对这种混乱使用的需要。

在JLS 4.11中使用它的用法- 使用的类型 :

  void loop(S s) { this.loop(s); } 

…以及JLS 15.12中方法调用中允许这种原因的正式定义- 方法调用表达式 。 对于更具体的细节,您可以跳至15.12.2.7和15.12.2.8。 15.12.2.8 – 推断未解决的类型参数解释了其运行的forms逻辑。

来自Java Generics and Collections

 List ints = Lists.toList(); // first example List objs = Lists.toList(1, "two"); // second example 
  1. In the first example ,如果没有type参数,则Sun编译器使用的类型推断算法的信息太少,无法推断出正确的类型。 它推断toList的参数是一个任意generics类型的空数组,而不是一个空的整数数组,这会触发前面描述的未经检查的警告。 (Eclipse编译器使用不同的推理算法,并在没有显式参数的情况下正确编译相同的行。)
  2. In the second example ,没有类型参数,类型推断算法的信息太多,无法推断出正确的类型。 您可能认为Object是整数和字符串共有的唯一类型,但实际上它们也都实现了Serializable和Comparable接口。 类型推断算法无法选择这三种中的哪一种是正确的类型。

通常,以下经验法则足够:

在对generics方法的调用中,如果有一个或多个参数对应于类型参数并且它们都具有相同的类型,则可以推断出类型参数; 如果没有与type参数对应的参数或者参数属于预期类型的​​不同子类型,则必须明确给出type参数。

传递类型参数的一些要点

将类型参数传递给generics方法调用时,它将显示在左侧的尖括号中,就像在方法声明中一样。

Java语法要求类型参数可能仅出现在使用虚线forms的方法调用中。 即使toList的方法是在调用代码的同一个类中定义的,我们也不能按如下方式缩短它:

 List ints = toList(); // compile-time error 

这是非法的,因为它会使解析器混淆。