28

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

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.

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.

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;
    }
Edward Falk
  • 9,578
  • 8
  • 66
  • 105
Horse Voice
  • 7,218
  • 14
  • 56
  • 111

5 Answers5

27

You more often think of classes as being generic, but methods can be generic too. A common example is 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:

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");
Chris Martin
  • 28,558
  • 6
  • 66
  • 126
  • But why does the space work? There should not be a space right? – Horse Voice Jan 23 '14 at 02:24
  • 6
    That's just a matter of convention. There are a lot of places where it's valid to insert superfluous whitespace if you want. You could also write `List < String > stooges = Arrays . < String > asList ( "Larry" , "Moe" , "Curly" ) ;`. You could even insert line breaks in any of those places as well. ` – Chris Martin Jan 23 '14 at 02:25
  • Thanks. I forgot about the white space is matter of preference – Horse Voice Jan 23 '14 at 02:37
12

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>>.

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

Community
  • 1
  • 1
assylias
  • 297,541
  • 71
  • 621
  • 741
  • 2
    Didn't know it was called a "type witness" +1 – Paul Bellora Jan 23 '14 at 15:51
  • So, this would be correct except the method actually is taking the generic type as an arg so it doesn't need to infer from the target – Brian Roach Jan 23 '14 at 20:56
  • what is target typing? what is type witness? – Horse Voice Jan 27 '14 at 00:06
  • A type witness is when you need to add the generic type explicitly in the method call as in your example. Target typing is the compiler being able to determine the generic type of a method call based on the type of the variable that the result is assigned to. The [tutorial on type inference](http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html) has a more detailed explanation. – assylias Jan 27 '14 at 07:18
3

It's ugly, but valid.

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

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.

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

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.

             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>

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()

Brian Roach
  • 72,790
  • 10
  • 128
  • 154
  • I don't understand. could you please elaborate. I've add the ConnectionHelper class for your reference. thank you. – Horse Voice Jan 23 '14 at 02:16
2

From Java Generics and 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. 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.

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.

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:

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

This is illegal because it will confuse the parser.

Prateek
  • 11,146
  • 11
  • 54
  • 77
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.

You can see an official example of it here.

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

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.

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

<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.

asteri
  • 10,842
  • 12
  • 53
  • 82
  • It's not really inference anymore if it's supplied explicitly. – Nate C-K Jan 23 '14 at 02:18
  • @NateC-K Haha, good point about the English language. Still considered Type Inference for Java, though, even if it doesn't make sense (as you can see in the Oracle tutorials). :) – asteri Jan 23 '14 at 02:20