为什么 picocli 需要 ArgGroup 中的参数,即使默认多重性为 0..1?

Why is picocli requiring args within an ArgGroup even with the default multiplicity of 0..1?

您可以在此处找到代码示例。 Link to my GitHub project

在文件Driver.java中,可以看到我指定了一个独占的ArgGroup。我根据文档的理解是默认的多重性是 0..1。文档指出,"The default is multiplicity = "0..1",这意味着默认情况下可以省略或指定一个组。

我也曾尝试将多重性显式设置为 0..1,但这并没有改变行为。 运行 没有 -al 或 -rl 选项的程序,解析会抛出 NullPointerException。该框架的行为就好像这些选项之一是必需的。那是不符合documentation的。如果我愿意,我应该能够 运行 这个程序只用 -n 选项。我希望 ArgGroup 完全可选。

git 中心 link 的程序是一个功能齐全的 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.access0(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)

感谢您添加堆栈跟踪。我看到 NullPointerException 出现在 line 58call 方法中,而不是 picocli 本身。

所以问题不在于 picocli 需要可选的 (multiplicity = 0..1) 参数组中的选项,问题在于 call 方法假定 @ArgGroup-注释字段将始终被初始化,即使组中没有匹配的选项。这个假设是不正确的。

发生的情况是,如果在命令行中既没有指定 -al 选项也没有指定 -rl 选项,那么 SynchronizationOptions 参数组根本就没有匹配项,所以 picocli 不会实例化 SynchronizationOptions 对象,并且 synchOptions field on line 32 不会被初始化。

这就是 picocli 解析器处理参数组的方式:例如,对于具有多重性的组 *,picocli 将为每个组匹配创建一个用户对象实例,并将其添加到带注释的 collection/array字段。

如果没有组匹配,则用户对象的实例为零。这允许应用程序准确检测组是否匹配 - 如果组 匹配,应用程序可以依赖于排他组 的不变量一个 选项匹配并具有值,并且对于同现组 all 选项匹配并具有来自命令行的值。 (如果 picocli 在没有匹配的情况下实例化用户对象,这将是不可能的。)

解决方案是将应用程序更改为检查 null 或初始化应用程序中的 synchOptions 字段。后者可能是最简单和最干净的。例如,替换:

@ArgGroup(exclusive = true)
SynchronizationOptions synchOptions;

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

然后 synchOptions 永远不会 null 因此应用程序可以安全地引用其在 call 方法中的字段:

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

或者,检查call方法中是否有synchOptions == null。这允许应用程序检测是否有任何同步选项匹配,如果匹配,应用程序可以依赖至少一个布尔字段是 true.

这一事实