创建一个新的 class "on the fly"?
Create a new class "on the fly"?
...特别是在 Groovy(因此标记)?
在 Java 中你不能这样做......但在动态语言中(例如 Python)你通常可以。
在 Eclipse 中,试图在 Spock 功能(即测试方法)的 given
块中做类似的事情:
Groovy:Class definition not expected here. Please define the class at
an appropriate place or perhaps try using a block/Closure instead.
... "appropriate" 地方显然在功能之外。这会很笨重,而不是 groovy。使用 Groovy 几个月后,我觉得 Groovy 应该在什么时候提供更棒的东西。
假设我想扩展我的 abstract
class AbstractFoo
并在我的功能中创建一个新的子class Foo
,是有什么方法可以 "use a block/Closure" 实现这样的目标吗?
您可以通过实例化 AbstractFoo
并提供抽象方法的内联实现来简单地创建匿名 class。考虑以下示例:
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
class 来使它 "groovier" 多一点,如下所示:
def foo3 = { "test 123" } as AbstractFoo
foo3.bar()
在这种情况下,闭包 returns "test 123" 提供抽象 text()
方法的实现。如果您的抽象 class 只有一个抽象方法,它就会像这样工作。
具有多个抽象方法的抽象 class
但是如果一个抽象 class 有多个我们想要动态实现的抽象方法会怎样?在这种情况下,我们可以将此方法的实现作为映射提供,其中键是抽象方法的名称,值是提供实现的闭包。让我们看看下面的例子:
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()
这个例子使用了一个抽象 class 和两个抽象方法。我们可以通过将 Map<String, Closure<?>>
类型的映射转换为 AbstractFoo
class 来实例化此 class。 运行 此示例向控制台生成以下输出:
text: test 1, number: 23
正在 Groovy
中即时创建非匿名 classes
Groovy 还允许您创建 class 例如来自使用 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
的非匿名 class,它扩展了 AbstractFoo
并提供了 test()
方法的定义。这种方法很容易出错,因为您将新的 class 定义为字符串,所以忘记任何 IDE 捕获错误和警告的支持。
在测试规范中提供子class
您最初的问题提到了尝试在 given:
Spock 块中为规范创建 class。我强烈建议使用最简单的可用工具——创建一个嵌套的私有静态 class,这样您就可以在测试中轻松访问它,而不会在测试之外公开它。像这样:
class MySpec extends Specification {
def "should do something"() {
given:
Class<?> clazz = Foo.class
when:
//....
then:
///....
}
private static class Foo extends AbstractFoo {
}
}
...特别是在 Groovy(因此标记)?
在 Java 中你不能这样做......但在动态语言中(例如 Python)你通常可以。
在 Eclipse 中,试图在 Spock 功能(即测试方法)的 given
块中做类似的事情:
Groovy:Class definition not expected here. Please define the class at an appropriate place or perhaps try using a block/Closure instead.
... "appropriate" 地方显然在功能之外。这会很笨重,而不是 groovy。使用 Groovy 几个月后,我觉得 Groovy 应该在什么时候提供更棒的东西。
假设我想扩展我的 abstract
class AbstractFoo
并在我的功能中创建一个新的子class Foo
,是有什么方法可以 "use a block/Closure" 实现这样的目标吗?
您可以通过实例化 AbstractFoo
并提供抽象方法的内联实现来简单地创建匿名 class。考虑以下示例:
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
class 来使它 "groovier" 多一点,如下所示:
def foo3 = { "test 123" } as AbstractFoo
foo3.bar()
在这种情况下,闭包 returns "test 123" 提供抽象 text()
方法的实现。如果您的抽象 class 只有一个抽象方法,它就会像这样工作。
具有多个抽象方法的抽象 class
但是如果一个抽象 class 有多个我们想要动态实现的抽象方法会怎样?在这种情况下,我们可以将此方法的实现作为映射提供,其中键是抽象方法的名称,值是提供实现的闭包。让我们看看下面的例子:
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()
这个例子使用了一个抽象 class 和两个抽象方法。我们可以通过将 Map<String, Closure<?>>
类型的映射转换为 AbstractFoo
class 来实例化此 class。 运行 此示例向控制台生成以下输出:
text: test 1, number: 23
正在 Groovy
中即时创建非匿名 classesGroovy 还允许您创建 class 例如来自使用 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
的非匿名 class,它扩展了 AbstractFoo
并提供了 test()
方法的定义。这种方法很容易出错,因为您将新的 class 定义为字符串,所以忘记任何 IDE 捕获错误和警告的支持。
在测试规范中提供子class
您最初的问题提到了尝试在 given:
Spock 块中为规范创建 class。我强烈建议使用最简单的可用工具——创建一个嵌套的私有静态 class,这样您就可以在测试中轻松访问它,而不会在测试之外公开它。像这样:
class MySpec extends Specification {
def "should do something"() {
given:
Class<?> clazz = Foo.class
when:
//....
then:
///....
}
private static class Foo extends AbstractFoo {
}
}