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

时间:2021-09-01 09:42:51

I came across some advanced java code (advanced for me :) ) I need help understanding.

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

In a class there is a nested class as below:

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

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);
    }
}

Later in the outer class, there is an instance of the callable class being created. I have no idea what this is:

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

ConnectionHelper.<List<ClientCoverageCRU>> tryExecute(coverageCRUDaoCallable);

It doesn't look like java syntax to me. Could you please elaborate what's going on in this cryptic syntax? You can see it being used below in the code excerpt.

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

CoverageCRUDaoCallable coverageCRUDaoCallable = new CoverageCRUDaoCallable(
        dalClient.getOid(), sourceContextId);

// use Connection helper to make coverageCRUDao call.
List<ClientCoverageCRU> coverageCRUList = ConnectionHelper
        .<List<ClientCoverageCRU>> tryExecute(coverageCRUDaoCallable);

EDITED added the ConnectionHelper class.

EDITED添加了ConnectionHelper类。

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;
    }

5 个解决方案

#1


22  

You more often think of classes as being generic, but methods can be generic too. A common example is Arrays.asList.

您经常将类视为通用类,但方法也可以是通用的。一个常见的例子是Arrays.asList。

Most of the time, you don't have to use the syntax with angle brackets <...>, even when you're invoking a generic method, because this is the one place in which the Java compiler is actually capable of doing basic type inference in some circumstances. For example, the snippet given in the Arrays.asList documentation omits the type:

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

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

But it's equivalent to this version in which the generic type is given explicitly:

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

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

#2


9  

That is because, until Java 7, generics do not fully support target typing, so you need to help the compiler a little with what is called a type witness like in ConnectionHelper.<List<ClientCoverageCRU>>.

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

Note however that Java 8 significantly improves target typing and in your specific example the type witness is not required in Java 8.

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

#3


3  

It's ugly, but valid.

这很难看,但有效。

Whatever ConnectionHelper is, it has a static tryExecute method that needs to infer a generic type.

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

Something like:

public static <T> T tryExecute() { ... }

Edit from updated Question: Java has type inference for generic types. The first <T> in the method signature signifies the type will be inferred when the method is called.

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

In your updated post you show tryExecute() defined to take a generic argument:

在您更新的帖子中,您显示了定义的tryExecute()以获取泛型参数:

public static <T> T tryExecute(Callable<T> command)

This actually means the use of that syntax is completely redundant and unnecessary; T (the type) is inferred from the command being passed in which has to implement Callable<T>. The method is defined to return something of the inferred type T.

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

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

In your example, coverageCRUDaoCallable has to be implementing Callable<List<ClientCoverageCRU>> because the method is returning List<ClientCoverageCRU>

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

In my example above you'd have to use the syntax you were asking about because nothing is being passed in from which to infer the type. T has to be explicitly provided via using ConnectionHelper.<List<ClientCoverageCRU>>tryExecute()

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

#4


1  

So basically, the tryExecute() method in the ConnectionHelper uses generics. This allows you to feed the type inference to it prior to the method call after the "dot operator". This is actually shown directly in the Oracle Java tutorials for Generics, even though I'd consider it bad practice in a production environment.

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

You can see an official example of it here.

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

As you can see in your modified post, the tryExecute() definition is:

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

public static <T> T tryExecute(Callable<T> command)

By calling it as such (<List<ClientCoverageCRU>> tryExcute), you are forcing T to be a List<ClientCoverageCRU>. A better practice in general, though, would be to let this be inferred from an actual argument in the method. The type can also be inferred from the Callable<T>, so supplying it a Callable<List<ClientCoverageCRU>> as an argument would eliminate the need for this confusing usage.

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

See its usage in the JLS 4.11 - Where Types Are Used:

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

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

... and the formal definition of why this is allowed in method invocation in JLS 15.12 - Method Invocation Expressions. You can skip down to 15.12.2.7 and 15.12.2.8 for still more specifics. 15.12.2.8 - Inferring Unresolved Type Arguments explains the formal logic by which this functions.

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

#5


1  

From Java Generics and Collections,

来自Java Generics和Collections,

List<Integer> ints = Lists.<Integer>toList(); // first example
List<Object> objs = Lists.<Object>toList(1, "two"); // second example
  1. In the first example, without the type parameter there is too little information for the type inference algorithm used by Sun's compiler to infer the correct type. It infers that the argument to toList is an empty array of an arbitrary generic type rather than an empty array of integers, and this triggers the unchecked warning described earlier. (The Eclipse compiler uses a different inference algorithm, and compiles the same line correctly without the explicit parameter.)
  2. 在第一个示例中,如果没有type参数,则Sun编译器使用的类型推断算法的信息太少,无法推断出正确的类型。它推断toList的参数是一个任意泛型类型的空数组,而不是一个空的整数数组,这会触发前面描述的未经检查的警告。 (Eclipse编译器使用不同的推理算法,并在没有显式参数的情况下正确编译相同的行。)

  3. In the second example, without the type parameter there is too much information for the type inference algorithm to infer the correct type. You might think that Object is the only type that an integer and a string have in common, but in fact they also both implement the interfaces Serializable and Comparable. The type inference algorithm cannot choose which of these three is the correct type.
  4. 在第二个示例中,没有类型参数,类型推断算法的信息太多,无法推断出正确的类型。您可能认为Object是整数和字符串共有的唯一类型,但实际上它们也都实现了Serializable和Comparable接口。类型推断算法无法选择这三种中的哪一种是正确的类型。

In general, the following rule of thumb suffices:

In a call to a generic method, if there are one or more arguments that correspond to a type parameter and they all have the same type then the type parameter may be inferred; if there are no arguments that correspond to the type parameter or the arguments belong to different subtypes of the intended type then the type parameter must be given explicitly.

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

Some points for passing type parameter

传递类型参数的一些要点

When a type parameter is passed to a generic method invocation, it appears in angle brackets to the left, just as in the method declaration.

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

The Java grammar requires that type parameters may appear only in method invocations that use a dotted form. Even if the method toList is defined in the same class that invokes the code, we cannot shorten it as follows:

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

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

This is illegal because it will confuse the parser.

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

#1


22  

You more often think of classes as being generic, but methods can be generic too. A common example is Arrays.asList.

您经常将类视为通用类,但方法也可以是通用的。一个常见的例子是Arrays.asList。

Most of the time, you don't have to use the syntax with angle brackets <...>, even when you're invoking a generic method, because this is the one place in which the Java compiler is actually capable of doing basic type inference in some circumstances. For example, the snippet given in the Arrays.asList documentation omits the type:

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

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

But it's equivalent to this version in which the generic type is given explicitly:

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

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

#2


9  

That is because, until Java 7, generics do not fully support target typing, so you need to help the compiler a little with what is called a type witness like in ConnectionHelper.<List<ClientCoverageCRU>>.

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

Note however that Java 8 significantly improves target typing and in your specific example the type witness is not required in Java 8.

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

#3


3  

It's ugly, but valid.

这很难看,但有效。

Whatever ConnectionHelper is, it has a static tryExecute method that needs to infer a generic type.

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

Something like:

public static <T> T tryExecute() { ... }

Edit from updated Question: Java has type inference for generic types. The first <T> in the method signature signifies the type will be inferred when the method is called.

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

In your updated post you show tryExecute() defined to take a generic argument:

在您更新的帖子中,您显示了定义的tryExecute()以获取泛型参数:

public static <T> T tryExecute(Callable<T> command)

This actually means the use of that syntax is completely redundant and unnecessary; T (the type) is inferred from the command being passed in which has to implement Callable<T>. The method is defined to return something of the inferred type T.

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

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

In your example, coverageCRUDaoCallable has to be implementing Callable<List<ClientCoverageCRU>> because the method is returning List<ClientCoverageCRU>

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

In my example above you'd have to use the syntax you were asking about because nothing is being passed in from which to infer the type. T has to be explicitly provided via using ConnectionHelper.<List<ClientCoverageCRU>>tryExecute()

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

#4


1  

So basically, the tryExecute() method in the ConnectionHelper uses generics. This allows you to feed the type inference to it prior to the method call after the "dot operator". This is actually shown directly in the Oracle Java tutorials for Generics, even though I'd consider it bad practice in a production environment.

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

You can see an official example of it here.

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

As you can see in your modified post, the tryExecute() definition is:

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

public static <T> T tryExecute(Callable<T> command)

By calling it as such (<List<ClientCoverageCRU>> tryExcute), you are forcing T to be a List<ClientCoverageCRU>. A better practice in general, though, would be to let this be inferred from an actual argument in the method. The type can also be inferred from the Callable<T>, so supplying it a Callable<List<ClientCoverageCRU>> as an argument would eliminate the need for this confusing usage.

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

See its usage in the JLS 4.11 - Where Types Are Used:

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

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

... and the formal definition of why this is allowed in method invocation in JLS 15.12 - Method Invocation Expressions. You can skip down to 15.12.2.7 and 15.12.2.8 for still more specifics. 15.12.2.8 - Inferring Unresolved Type Arguments explains the formal logic by which this functions.

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

#5


1  

From Java Generics and Collections,

来自Java Generics和Collections,

List<Integer> ints = Lists.<Integer>toList(); // first example
List<Object> objs = Lists.<Object>toList(1, "two"); // second example
  1. In the first example, without the type parameter there is too little information for the type inference algorithm used by Sun's compiler to infer the correct type. It infers that the argument to toList is an empty array of an arbitrary generic type rather than an empty array of integers, and this triggers the unchecked warning described earlier. (The Eclipse compiler uses a different inference algorithm, and compiles the same line correctly without the explicit parameter.)
  2. 在第一个示例中,如果没有type参数,则Sun编译器使用的类型推断算法的信息太少,无法推断出正确的类型。它推断toList的参数是一个任意泛型类型的空数组,而不是一个空的整数数组,这会触发前面描述的未经检查的警告。 (Eclipse编译器使用不同的推理算法,并在没有显式参数的情况下正确编译相同的行。)

  3. In the second example, without the type parameter there is too much information for the type inference algorithm to infer the correct type. You might think that Object is the only type that an integer and a string have in common, but in fact they also both implement the interfaces Serializable and Comparable. The type inference algorithm cannot choose which of these three is the correct type.
  4. 在第二个示例中,没有类型参数,类型推断算法的信息太多,无法推断出正确的类型。您可能认为Object是整数和字符串共有的唯一类型,但实际上它们也都实现了Serializable和Comparable接口。类型推断算法无法选择这三种中的哪一种是正确的类型。

In general, the following rule of thumb suffices:

In a call to a generic method, if there are one or more arguments that correspond to a type parameter and they all have the same type then the type parameter may be inferred; if there are no arguments that correspond to the type parameter or the arguments belong to different subtypes of the intended type then the type parameter must be given explicitly.

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

Some points for passing type parameter

传递类型参数的一些要点

When a type parameter is passed to a generic method invocation, it appears in angle brackets to the left, just as in the method declaration.

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

The Java grammar requires that type parameters may appear only in method invocations that use a dotted form. Even if the method toList is defined in the same class that invokes the code, we cannot shorten it as follows:

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

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

This is illegal because it will confuse the parser.

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