使用点运算符在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()
基本上, ConnectionHelper
的tryExecute()
方法使用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
-
In the first example
,如果没有type参数,则Sun编译器使用的类型推断算法的信息太少,无法推断出正确的类型。 它推断toList的参数是一个任意generics类型的空数组,而不是一个空的整数数组,这会触发前面描述的未经检查的警告。 (Eclipse编译器使用不同的推理算法,并在没有显式参数的情况下正确编译相同的行。) -
In the second example
,没有类型参数,类型推断算法的信息太多,无法推断出正确的类型。 您可能认为Object是整数和字符串共有的唯一类型,但实际上它们也都实现了Serializable和Comparable接口。 类型推断算法无法选择这三种中的哪一种是正确的类型。
通常,以下经验法则足够:
在对generics方法的调用中,如果有一个或多个参数对应于类型参数并且它们都具有相同的类型,则可以推断出类型参数; 如果没有与type参数对应的参数或者参数属于预期类型的不同子类型,则必须明确给出type参数。
传递类型参数的一些要点
将类型参数传递给generics方法调用时,它将显示在左侧的尖括号中,就像在方法声明中一样。
Java语法要求类型参数可能仅出现在使用虚线forms的方法调用中。 即使toList的方法是在调用代码的同一个类中定义的,我们也不能按如下方式缩短它:
List ints = toList(); // compile-time error
这是非法的,因为它会使解析器混淆。