gpt4 book ai didi

groovy - 如何使用 Python 风格的 kwargs 和默认值进行 Groovy 方法签名?

转载 作者:行者123 更新时间:2023-12-05 00:18:27 33 4
gpt4 key购买 nike

我可能要求太多了,但 Groovy 似乎非常灵活,所以这里...

我希望类中的方法像这样定义:

class Foo {

Boolean y = SomeOtherClass.DEFAULT_Y
Boolean z = SomeOtherClass.DEFAULT_Z

void bar(String x = SomeOtherClass.DEFAULT_X,
Integer y = this.y, Boolean z = this.z) {
// ...
}

}

并且能够仅提供某些参数,如下所示:
def f = new Foo(y: 16)
f.bar(z: true) // <-- This line throws groovy.lang.MissingMethodException!

我试图提供一个既灵活又类型安全的 API,这就是问题所在。给定的代码不灵活,因为我必须传入(并且作为 API 的用户知道) x 的默认值。以便调用该方法。以下是我想要的解决方案的一些挑战:
  • 类型安全是必须的--否 void bar(Map)签名,除非可以以某种方式使 key 类型安全。我意识到我可以在方法体中进行类型检查,但我试图避免这种级别的冗余,因为我有许多这种“类型”的方法要编写。
  • 我可以为每个方法签名使用一个类——类似于:
    class BarArgs {
    String x = SomeOtherClass.DEFAULT_X
    String y
    String z
    }

    并将其定义为:
    void bar(BarArgs barArgs) {
    // ...
    }

    并使用 map 构造函数使用我想要的方式调用它:f.bar(z: true) ,但我的问题在于对象的默认值 y .没有办法处理(我知道的),而不必在调用方法时指定它,如:f.bar(y: f.y, z: true) .这对我的小样本来说很好,但我正在查看某些方法的 20-30 个可选参数。

  • 欢迎任何建议(或如果需要的问题)!感谢您的浏览。

    最佳答案

    有趣的问题。我已经这样解释了你的要求

  • 该类应该有一组默认属性。
  • 每个方法都应该有一组默认参数。
  • 方法默认值覆盖类默认值。
  • 每个方法都可以有额外的参数,而不存在于类中。
  • 方法参数不应修改类实例。
  • 需要检查提供的参数的类型。

  • 我不确定数字 5,因为它没有明确指定,但它
    看起来这就是你想要的。

    据我所知,groovy 没有内置任何东西来支持这一切,
    但是有几种方法可以使其以“简单易用”的方式工作。

    想到的一种方法是创建专门的参数类,但是
    只使用映射作为方法中的参数。用一个简单的父类(super class)
    或 trait 来验证和设置属性,它是一个单行获取
    每个方法的实际参数。

    这是一个特征和一些可以用作起点的例子:
    trait DefaultArgs {
    void setArgs(Map args, DefaultArgs defaultArgs) {
    if (defaultArgs) {
    setArgs(defaultArgs.toArgsMap())
    }
    setArgs(args)
    }

    void setArgs(Map args) {
    MetaClass thisMetaClass = getMetaClass()
    args.each { name, value ->
    assert name instanceof String
    MetaProperty metaProperty = thisMetaClass.getMetaProperty(name)
    assert name && metaProperty != null
    if (value != null) {
    assert metaProperty.type.isAssignableFrom(value.class)
    }
    thisMetaClass.setProperty(this, name, value)
    }
    }

    Map toArgsMap() {
    def properties = getProperties()
    properties.remove('class')
    return properties
    }
    }

    有了这个特性,就可以很容易地创建专门的参数类。
    @ToString(includePackage = false, includeNames = true)
    class FooArgs implements DefaultArgs {
    String a = 'a'
    Boolean b = true
    Integer i = 42

    FooArgs(Map args = [:], DefaultArgs defaultArgs = null) {
    setArgs(args, defaultArgs)
    }
    }

    @ToString(includePackage = false, includeNames = true, includeSuper = true)
    class BarArgs extends FooArgs {
    Long l = 10

    BarArgs(Map args = [:], FooArgs defaultArgs = null) {
    setArgs(args, defaultArgs)
    }
    }

    以及使用这些参数的类:
    class Foo {
    FooArgs defaultArgs

    Foo(Map args = [:]) {
    defaultArgs = new FooArgs(args)
    }

    void foo(Map args = [:]) {
    FooArgs fooArgs = new FooArgs(args, defaultArgs)
    println fooArgs
    }

    void bar(Map args = [:]) {
    BarArgs barArgs = new BarArgs(args, defaultArgs)
    println barArgs
    }
    }

    最后,一个简单的测试脚本;注释中方法调用的输出
    def foo = new Foo()
    foo.foo() // FooArgs(a:a, b:true, i:42)
    foo.foo(a:'A') // FooArgs(a:A, b:true, i:42)
    foo.bar() // BarArgs(l:10, super:FooArgs(a:a, b:true, i:42))
    foo.bar(i:1000, a:'H') // BarArgs(l:10, super:FooArgs(a:H, b:true, i:1000))
    foo.bar(l:50L) // BarArgs(l:50, super:FooArgs(a:a, b:true, i:42))

    def foo2 = new Foo(i:16)
    foo2.foo() // FooArgs(a:a, b:true, i:16)
    foo2.foo(a:'A') // FooArgs(a:A, b:true, i:16)
    foo2.bar() // BarArgs(l:10, super:FooArgs(a:a, b:true, i:16))
    foo2.bar(i:1000, a:'H') // BarArgs(l:10, super:FooArgs(a:H, b:true, i:1000))
    foo2.bar(l:50L) // BarArgs(l:50, super:FooArgs(a:a, b:true, i:16))

    def verifyError(Class thrownClass, Closure closure) {
    try {
    closure()
    assert "Expected thrown: $thrownClass" && false
    } catch (Throwable e) {
    assert e.class == thrownClass
    }
    }

    // Test exceptions on wrong type
    verifyError(PowerAssertionError) { foo.foo(a:5) }
    verifyError(PowerAssertionError) { foo.foo(b:'true') }
    verifyError(PowerAssertionError) { foo.bar(i:10L) } // long instead of integer
    verifyError(PowerAssertionError) { foo.bar(l:10) } // integer instead of long

    // Test exceptions on missing properties
    verifyError(PowerAssertionError) { foo.foo(nonExisting: 'hello') }
    verifyError(PowerAssertionError) { foo.bar(nonExisting: 'hello') }
    verifyError(PowerAssertionError) { foo.foo(l: 50L) } // 'l' does not exist on foo

    关于groovy - 如何使用 Python 风格的 kwargs 和默认值进行 Groovy 方法签名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37735927/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com