Подтвердить что ты не робот

Нечетный вызов метода в java с помощью оператора точек для доступа к универсальному списку

Я столкнулся с некоторым продвинутым 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);

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

Ответ 1

Вы чаще думаете о том, что классы являются общими, но методы также могут быть общими. Общим примером является Arrays.asList.

В большинстве случаев вам не нужно использовать синтаксис с угловыми скобками <...>, даже если вы вызываете общий метод, потому что это одно место, в котором Java-компилятор действительно способен выполнять вывода основного типа в некоторых случаях. Например, фрагмент, указанный в документации Arrays.asList, опускает тип:

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

Но это эквивалентно этой версии, в которой типичный тип задан явно:

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

Ответ 2

Это потому, что до Java 7 дженерики не полностью поддерживают целевую типизацию, поэтому вам нужно немного помочь компилятору с тем, что называется типом свидетеля, как в ConnectionHelper.<List<ClientCoverageCRU>>.

Обратите внимание, что Java 8 значительно улучшает настройку целевой страницы, и в вашем конкретном примере тип свидетеля не требуется в Java 8.

Ответ 3

Это некрасиво, но верно.

Независимо от ConnectionHelper, он имеет статический метод tryExecute, который должен вызывать общий тип.

Что-то вроде:

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

Изменить из обновленного вопроса: У Java есть вывод типа для общих типов. Первый <T> в сигнатуре метода означает, что тип будет выведен при вызове метода.

В обновленном сообщении вы показываете tryExecute(), определяемый для принятия общего аргумента:

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

Это фактически означает, что использование этого синтаксиса является полностью избыточным и ненужным; T (тип) выводится из передаваемого command, в котором необходимо реализовать Callable<T>. Метод определен как возвращающий что-то из предполагаемого типа 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()

Ответ 4

Таким образом, метод tryExecute() в ConnectionHelper использует обобщения. Это позволяет вам подать вывод типа к нему до вызова метода после "оператора точки". Это фактически показано непосредственно в обучающих программах Oracle Java для Generics, хотя я считаю это плохой практикой в ​​рабочей среде.

Вы можете увидеть официальный пример этого здесь.

Как вы можете видеть в своем измененном сообщении, определение 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 - Вывод аргументов неразрешенного типа объясняет формальную логику, с помощью которой это работает.

Ответ 5

От 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, без параметра типа есть слишком много информация для алгоритма вывода типа для вывода правильного тип. Вы можете подумать, что Object является единственным типом, который является целым числом и строка имеет общее, но на самом деле они также реализуют интерфейсы Serializable и Comparable. Вывод типа алгоритм не может выбрать, какой из этих трех является правильным типом.

В общем случае следующее правило:

При вызове общего метода, если являются одним или несколькими аргументами, которые соответствуют параметру типа, и все они имеют тот же тип, то параметр типа может быть выведен; если нет аргументов, которые соответствуют параметру типа или аргументы относятся к различным подтипам то тип параметра должен быть указан явно.

Некоторые точки для параметра типа передачи

Когда параметр типа передается вызову общего метода, он отображается под углом скобки слева, так же, как в объявлении метода.

В грамматике Java требуется, чтобы параметры типа могли появляться только в вызовах метода, которые используют пунктирную форму. Даже если метод toList определен в том же классе, который вызывает код, мы не можем сократите его следующим образом:

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

Это незаконно, потому что это путает парсер.