我可能会问太多,但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)
签名,除非密钥可以某种方式被设置为类型安全。我意识到这一点,我可以在方法体中进行类型检查,但我正在努力避免这种冗余程度,因为我有许多这种“类型”的方法要写。我可以为每个方法签名使用一个类 - 如:
class BarArgs {
String x = SomeOtherClass.DEFAULT_X
String y
String z
}
并将其定义为:
void bar(BarArgs barArgs) {
// ...
}
使用地图构造函数f.bar(z: true)
以我想要的方式调用它,但我的问题在于y
上对象的默认值。没有办法处理它(我知道),而不必在调用方法时指定它,如:f.bar(y: f.y, z: true)
。这对我的小样本很好,但是我在某些方法上看20-30个可选参数。
欢迎任何建议(或问题,如果需要)!谢谢你看看。
答案 0 :(得分:6)
有趣的问题。我已经解释了你的要求
我不确定数字5,因为没有明确指定,但它 看起来那就是你想要的。
据我所知,groovy中没有任何内置功能可以支持所有这些, 但有几种方法可以让它以“简单易用”的方式运作。
想到的一种方法是创建专门的参数类,但是 仅使用maps作为方法中的参数。用简单的超级课程 或者特性来验证和设置属性,这是一个单线来获得 每种方法的实际参数。
这是一个特点和一些可以作为起点的例子:
@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
最后,一个简单的测试脚本;注释中方法调用的输出
25 27 29 25 27 29 25 27 29 25 27 29 25 27 28