您可以通过实例化AbstractFoo 并提供抽象方法的内联实现来简单地创建一个匿名类。考虑以下示例:
abstract class AbstractFoo {
void bar() {
println text()
}
abstract String text()
}
def foo1 = new AbstractFoo() {
@Override
String text() {
return "Hello, world!"
}
}
def foo2 = new AbstractFoo() {
@Override
String text() {
return "Lorem ipsum dolor sit amet"
}
}
foo1.bar()
foo2.bar()
foo1 和 foo2 都实现了 AbstractFoo,它们提供了不同的 text() 方法实现,导致不同的 bar() 方法行为。运行这个 Groovy 脚本会在控制台产生以下输出:
Hello, world!
Lorem ipsum dolor sit amet
这与 Groovy 无关,您可以使用 Java 实现完全相同的行为。但是,您可以通过将闭包强制转换为 AbstractFoo 类使其更“时髦”,如下所示:
def foo3 = { "test 123" } as AbstractFoo
foo3.bar()
在这种情况下,返回“test 123”的闭包提供了抽象text() 方法的实现。如果你的抽象类只有一个抽象方法,它的工作原理就是这样。
具有多个抽象方法的抽象类
但是如果一个抽象类有多个我们想要动态实现的抽象方法会发生什么?在这种情况下,我们可以将这些方法的实现作为映射提供,其中键是抽象方法的名称,值是提供实现的闭包。让我们看看下面的例子:
abstract class AbstractFoo {
abstract String text()
abstract int number()
void bar() {
println "text: ${text()}, number: ${number()}"
}
}
def foo = [
text: { "test 1" },
number: { 23 }
] as AbstractFoo
foo.bar()
此示例使用具有两个抽象方法的抽象类。我们可以通过将Map<String, Closure<?>> 类型的映射转换为AbstractFoo 类来实例化这个类。运行此示例会在控制台产生以下输出:
text: test 1, number: 23
在 Groovy 中动态创建非匿名类
Groovy 还允许您创建一个类,例如使用GroovyClassLoader.parseClass(input) 方法从多行字符串中提取。让我们看看下面的例子:
abstract class AbstractFoo {
void bar() {
println text()
}
abstract String text()
}
def newClassDefinitionAsString = '''
class Foo extends AbstractFoo {
String text() {
return "test"
}
}
'''
def clazz = new GroovyClassLoader(getClass().getClassLoader()).parseClass(newClassDefinitionAsString)
def foo = ((AbstractFoo) clazz.newInstance())
foo.bar()
这里我们定义了一个名为Foo 的非匿名类,它扩展了AbstractFoo 并提供了test() 方法的定义。这种方法很容易出错,因为您将新类定义为 String,所以忘记任何 IDE 对捕获错误和警告的支持。
在测试规范中提供子类
您最初的问题提到了尝试为given: Spock 块中的规范创建类。我强烈建议使用最简单的可用工具——创建一个嵌套的私有静态类,这样您就可以在测试中轻松访问它,并且不会在测试之外公开它。像这样的:
class MySpec extends Specification {
def "should do something"() {
given:
Class<?> clazz = Foo.class
when:
//....
then:
///....
}
private static class Foo extends AbstractFoo {
}
}