【问题标题】:Why is picocli requiring args within an ArgGroup even with the default multiplicity of 0..1?为什么 picocli 需要 ArgGroup 中的 args,即使默认多重性为 0..1?
【发布时间】:2023-03-24 05:42:01
【问题描述】:

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

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

我还尝试将多重性显式设置为 0..1,但这并没有改变行为。在没有 -al 或 -rl 选项的情况下运行程序,解析会引发 NullPointerException。该框架的行为就像需要其中一个选项一样。这与documentation 不一致。如果我愿意,我应该可以只使用 -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)

【问题讨论】:

  • NullPointerException 听起来您可能在 picocli 中发现了一个错误。您介意在 picocli 问题跟踪器上提出问题吗?你实际看到的行为是什么?有堆栈跟踪吗?当我到达我的电脑时,我会尝试重现。
  • 我就是这么想的。我只是想先征求社区意见,以防我误解了文档。我发布了堆栈跟踪,希望我的期望很明确。另一个问题是,如果没有选项,我会得到一个异常,但如果我在没有选项的情况下运行,我仍然希望显示使用(帮助)信息。在我看来,arg 组本身应该是可选的,除非我指定需要它 1 次或更多次的多重性。我错了吗?
  • 感谢堆栈跟踪,这很有用!我更新了我的答案。如果应用程序在call 方法中检查null,则不会有异常。我不确定“如果我在没有选项的情况下运行,我仍然希望显示使用(帮助)信息”是什么意思。这听起来像您希望 picocli 不考虑任何选项无效输入并显示错误,但这不是错误:因为多重性是 0..1,所以 arg 组是可选的,所以不指定它很好,对吧?
  • 在我添加 arg 组之前,未在命令行上指定任何内容总是会导致自动显示帮助。我不需要输入 --help 来获取帮助信息。有了论据组,这种情况就停止了。自从添加了 arg 组后,它的工作方式似乎仍然不正确。
  • 对不起,我没有关注。我刚刚检查了 Driver 类,但我不明白为什么当用户没有在命令行上指定任何参数时会显示使用帮助消息。我看不到任何必需的参数。我错过了什么吗?

标签: picocli


【解决方案1】:

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

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

如果在命令行上既没有指定-al 选项也没有指定-rl 选项,那么会发生SynchronizationOptions 参数组根本不匹配的情况,因此picocli 不会实例化@987654332 @object 和 synchOptions field on line 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这一事实。

【讨论】:

  • 感谢您的回复。但是,对于 arg 组,默认多重性为 0..1,我的期望是 arg 组本身是可选的。因此,不指定任何一个选项应该没问题,并且有界字段是默认的初始化布尔值。
  • 但是,synchOptions 为 null 是正确的,这解释了异常,所以我可以检查 null 并跳过逻辑。我只是不认为我应该这样做。其中的布尔值是默认初始化的,因此基于我希望创建的文档,其中两个布尔值初始化为 false。
  • 感谢您阐明您的期望。我更新了我的答案,详细说明了 picocli 解析器何时为参数组实例化用户对象。也许用户手册也应该对此提供更多详细信息。
  • updated the user manual 澄清@ArgGroup 注释字段何时初始化。你能看看这是否足够?
  • 是的,这很有帮助。谢谢。
猜你喜欢
  • 2021-04-18
  • 2012-07-06
  • 2020-09-09
  • 2016-05-27
  • 1970-01-01
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多