【问题标题】:Create a new class "on the fly"?“即时”创建一个新课程?
【发布时间】:2018-06-09 07:43:28
【问题描述】:

...特别是在 Groovy 中(因此标记)?

在 Java 中你不能这样做......但在动态语言(例如 Python)中你通常可以。

在 Eclipse 中尝试在 Spock 功能(即测试方法)的 given 块中执行类似操作:

Groovy:此处不需要类定义。请在以下位置定义类 一个合适的地方,或者尝试使用块/闭包。

...一个“适当”的地方显然在功能之外。这将是笨重的,而不是时髦的。使用 Groovy 几个月后,我对 Groovy 什么时候应该提供更时髦的东西有了感觉。

所以说我想扩展我的abstractAbstractFoo 并创建一个新的子类Foo,在我的功能中,有没有办法“使用块/闭包”来实现类似的东西?

【问题讨论】:

  • @MichaelEaster 不要以为那是苍蝇。 titles 使它们看起来相同。但是看看这两个问题并告诉我它们是否相同。我正在寻找一种专门创建子类的方法,而不是通过使用一些巨大的机制。我的问题实际上是受到引用的 Eclipse 消息的启发。如果后者仅仅意味着 Szymon 提出的内容,那就有点令人失望了……

标签: groovy closures subclass


【解决方案1】:

您可以通过实例化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()

foo1foo2 都实现了 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  {

    }
}

【讨论】:

  • 谢谢。 ... as AbstractFoo 的东西对我来说是新的,但我知道的其他一切。以上都不允许创建/定义(非匿名)新类,但只能创建新对象/实例。
  • @mikerodent 知道了。那么为什么不使用嵌套的私有静态类呢?与在运行时动态生成 Groovy 类相比,这是最简单的方法并且更不容易出错(也添加了一个示例)
  • 是的,这就是我目前正在做的事情。虽然没有特别需要 static,也没有特别需要 private:在 Groovy 中,private 的唯一作用是对子类隐藏字段/方法/内部类。
  • 感谢您的努力...看起来{ ... } as AbstractFoo 已经做得很好了(除非某些超级极客有更好的主意...)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多