Почему picocli требует args в ArgGroup даже с кратностью по умолчанию 0..1? - PullRequest
1 голос
/ 27 октября 2019

Пример кода вы можете найти здесь. Ссылка на мой проект GitHub

В файле Driver.java вы можете видеть, что я указал эксклюзивную ArgGroup. На основании документации я понимаю, что кратность по умолчанию равна 0..1. В документации говорится: «По умолчанию это кратность =« 0..1 », что означает, что по умолчанию группа может быть опущена или указана один раз».

Я также попытался явно установить множественность в 0 .. 1 но это не изменило поведение. При запуске программы без параметров -al или -rl при синтаксическом анализе возникает исключение NullPointerException. Каркас ведет себя так, как будто требуется один из этих вариантов. Это не соответствует документации . Я должен иметь возможность запускать эту программу только с опцией -n, если захочу. Я хочу, чтобы ArgGroup была полностью необязательной.

Программа по ссылке git hub - это полностью функционирующий проект maven, который можно клонировать, собрать и запустить. Однако здесь есть трассировка стека. Без заданных аргументов или без группы arg. Я ожидаю, что без каких-либо аргументов информация об использовании будет напечатана. Кроме того, кратность по умолчанию для группы должна составлять 0..1, поэтому мне не нужно указывать один из параметров в группе arg.

java.lang.NullPointerException
    at com.shawnfox.java4.concurrency.Driver.call(Driver.java:58)
    at com.shawnfox.java4.concurrency.Driver.call(Driver.java:1)
    at picocli.CommandLine.executeUserObject(CommandLine.java:1743)
    at picocli.CommandLine.access$900(CommandLine.java:145)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2101)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2068)
    at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:1935)
    at picocli.CommandLine.execute(CommandLine.java:1864)
    at com.shawnfox.java4.concurrency.Driver.main(Driver.java:50)

1 Ответ

1 голос
/ 27 октября 2019

Спасибо за добавление трассировки стека. Я вижу, что NullPointerException происходит в методе call в строке 58 , а не в самой picocli.

Таким образом, проблема не в том, что picocli требуются параметры в необязательной группе аргументов (кратность = 0..1), проблема в том, что метод call предполагает, что аннотированное поле @ArgGroup всегда будет инициализировано, даже если ни одна опция в группе не совпадает. Это предположение неверно.

Что происходит, если ни параметр -al, ни параметр -rl не указаны в командной строке, то для группы аргументов SynchronizationOptions вообще нет соответствия,поэтому picocli не будет создавать экземпляр объекта SynchronizationOptions, а поле synchOptions в строке 32 не будет инициализировано.

Так работает анализатор picocli с группами аргументов: например, для группы с кратностью * picocli создаст экземпляр объекта пользователя для каждого совпадения группы и добавит его в аннотированную коллекцию /поле массива.

Если не найдено ни одной группы, экземпляры пользовательского объекта будут нулевыми. Это позволяет приложению точно определять, была ли группа сопоставлена ​​или нет - и если группа соответствует , приложение может полагаться на инвариант, который для исключительной группы был только один параметр соответствует и имеет значение, а для совместной группы все параметры были сопоставлены и имеют значения из командной строки. (Это было бы невозможно, если picocli создал экземпляр пользовательского объекта без совпадения.)

Решение состоит в том, чтобы изменить приложение, чтобы либо проверять наличие null, либо инициализировать поле synchOptions в приложении. Последнее, наверное, самое простое и чистое. Например, замените:

@ArgGroup(exclusive = true)
SynchronizationOptions synchOptions;

на

@ArgGroup(exclusive = true)
SynchronizationOptions synchOptions = new SynchronizationOptions();

Тогда synchOptions никогда не будет null, поэтому приложение может безопасно ссылаться на свои поля в методе call:

public Void call() {
    if (synchOptions.useReentrantLock) {
        // ...

Либо проверьте, есть ли synchOptions == null в методе call. Это позволяет приложению обнаруживать, был ли сопоставлен какой-либо из параметров синхронизации, и если оно было сопоставлено, приложение может полагаться на тот факт, что хотя бы одно из логических полей имеет значение true.

...