动态创建Groovy类

给定一个类名,我想动态创建一个Groovy类,为它添加属性和方法。 我使用创建新类

instance = this.class.classLoader.parseClass( "public class $name {}") 

对于我使用的方法

 instance.metaClass."$it.key" = it.value 

其中it.key是一个字符串(方法名称),it.value是一个闭包。 这很方便,因为我可以指定方法参数类型并获取类型检查。 但是,如果没有为其赋值,我无法指定动态创建的属性类型。 我可以通过明确定义属性的getter和setter来解决这个问题。 这样可行,但似乎metaClass.name = value和metaClass.getName = {}实际上都没有在类中创建一个字段,因为Java字段运算符不适用于创建的属性。 我可以将属性添加到Groovy类并指定其类型,而无需为其分配初始值或显式定义getter和setter方法吗? 有没有办法向Groovy类添加新字段? 这是脚本:

 class SomeClass { Integer p1 String p2 } class ClassBuilder { def name def instance def properties def methods def ClassBuilder() { properties = [:] methods = [:] } def set_name(name) { this.name = name } def add_property(name, type) { properties[name] = type } def add_method(name, closure) { methods[name] = closure } def get_instance() { instance = this.class.classLoader.parseClass( "public class $name {}") properties.each { instance.metaClass."$it.key" = null //doesn't work instance.metaClass."$it.key".type = it.value } methods.each { instance.metaClass."$it.key" = it.value } return instance } } builder = new ClassBuilder() builder.set_name('MyClass') builder.add_property('property1', String) builder.add_property('property2', SomeClass) builder.add_method('method1', {SomeClass obj -> println obj}) builder.add_method('setProperty2', {SomeClass obj -> this.property2 = obj}) builder.add_method('getProperty2', {return this.property2}) builder.add_method('method2', {return property1 + property2}) c = builder.get_instance() i = c.newInstance() i.property1 = new SomeClass() i.property2 = 5 //i.method2() //throws GroovyCastException //i.property2 = 'throws GroovyCastException' //i.@property1 = 'throws MissingFieldException' //No such field: property2 for class: MyClass //i.@property2 = new SomeClass() i.method1(new SomeClass()) //i.method1('throws MissingMethodException') 

[编辑]

用例是这样的:我在Java中定义了一个接口或基类。 用户在Groovy中实现接口或扩展基类,并将类传递回Java以供主应用程序使用。 用户不是程序员,因此他们使用简单的DSL定义类,并使用构建器构造实际的类。 我还在尝试使用Groovy / JRuby和Java interop(两种语言都是新的)。

通过使用GroovyClassLoader和SimpleTemplateEngine,我或多或少能够使它工作。 这是代码:

 class ClassBuilder { GroovyClassLoader loader String name Class cls def imports def fields def methods def ClassBuilder(GroovyClassLoader loader) { this.loader = loader imports = [] fields = [:] methods = [:] } def setName(String name) { this.name = name } def addImport(Class importClass) { imports << "${importClass.getPackage().getName()}" + ".${importClass.getSimpleName()}" } def addField(String name, Class type) { fields[name] = type.simpleName } def addMethod(String name, Closure closure) { methods[name] = closure } def getCreatedClass() { def templateText = ''' <%imports.each {%>import $it\n <% } %> class $name { <%fields.each {%> $it.value $it.key \n<% } %> } ''' def data = [name: name, imports: imports, fields: fields] def engine = new groovy.text.SimpleTemplateEngine() def template = engine.createTemplate(templateText) def result = template.make(data) cls = loader.parseClass(result.toString()) methods.each { cls.metaClass."$it.key" = it.value } return cls } } 

以下是我如何使用它动态创建类的示例:

 import java.util.Calendar def builder = new ClassBuilder(this.class.classLoader) builder.setName("MyClass"); builder.addImport(Calendar) builder.addField('field1', Integer) builder.addField('field2', Integer) builder.addMethod('sum') { field1 + field2 } builder.addMethod('product') { field1 * field2 } builder.addMethod('testCalendar') { println Calendar.getInstance().getTime() } Class myClass = builder.getCreatedClass() def myInstance = myClass.newInstance() myInstance.field1 = 1 myInstance.field2 = 2 println myInstance.sum() println myInstance.product() myInstance.setField2(1500) println myInstance.getField2() myInstance.testCalendar() 

一个不错的变体是使用GroovyShell。

 def c = """ package de.myCorp.test class Test { def a def b def c Test(String c){ this.c = c} public String greet(){ return "hello "+a } public void count(){ (1..4).each{ println it } } } def class UsingTest { Test mytest = null UsingTest (Test test){ this.mytest = test } def using(){ mytest.greet(); } } """ GroovyShell gs = new GroovyShell() //I hope this is not a too bad hack ^^ def erg = gs.evaluate(c+";['test':Test, 'using':UsingTest];") def testclass = erg["test"].newInstance("Charlotte on Ice") testclass.a = "hugo" testclass.b = "sepp" testclass.count() assert testclass.greet() == "hello hugo" assert testclass.c == "Charlotte on Ice" assert testclass.class.name == "de.myCorp.test.Test" def usingclass = erg['using'].newInstance(testclass) usingclass.mytest.a = "Fritzl" assert usingclass.using() == "hello Fritzl" 

…特别注意GroovyShell.evaluate(URI uri)类型…… GroovyShellDoc