给定一个类名,我想动态创建一个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(两种语言都是新的)。
答案 0 :(得分:6)
我或多或少能够通过使用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()
答案 1 :(得分:1)
一个不错的变体就是使用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