Вы можете просто создать анонимный класс, создав экземпляр 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()
В этом случае замыкание, которое возвращает «тест 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
class.Выполнение этого примера приводит к выводу на консоль следующих данных:
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:
блоке Спока.Я настоятельно рекомендую использовать самый простой из доступных инструментов - создание вложенного частного статического класса, чтобы вы могли легко получить к нему доступ внутри теста и не показывать его вне теста.Примерно так:
class MySpec extends Specification {
def "should do something"() {
given:
Class<?> clazz = Foo.class
when:
//....
then:
///....
}
private static class Foo extends AbstractFoo {
}
}