动态创建一个Groovy类 [英] Create a Groovy class dynamically

查看:178
本文介绍了动态创建一个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 {
整数p1
字符串p2


class ClassBuilder {
def name
def instance
def properties
def methods

def ClassBuilder() {
properties = [:]
methods = [:]
}

def set_name(name){
this.name = name

$ b $ 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
//不起作用
instance.metaClass。 $ it.key.type = it.value
}

methods.each {
inst ance.metaClass。$ it.key= it.value
}

返回实例
}
}

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()//引发GroovyCastException

// i .property2 ='抛出GroovyCastException'
//i.@property1 ='抛出MissingFieldException'

//没有这样的字段:class2的property2:MyClass
//i.@ property2 = new SomeClass()

i.method1(new SomeClass())
//i.method1('Throws MissingMethodException')



用例如下:I用Java定义一个接口或一个基类。用户在Groovy中实现接口或扩展基类,然后将该类传回给Java以供主应用程序使用。用户不是程序员,所以他们使用一个简单的DSL来定义这个类,并且我使用一个构建器来构造实际的类。我仍在尝试使用Groovy / JRuby和Java interop(这两种语言都是新的)。

解决方案

我或多或少地能够通过使用GroovyClassLoader和SimpleTemplateEngine来实现它。这里是代码:

  class ClassBuilder {

GroovyClassLoader loader
字符串名称
Class cls
def导入
def fields
def方法

def ClassBuilder(GroovyClassLoader loader){
this.loader = loader
进口= []
字段= [:]
方法= [:]
}

def setName(字符串名称){
this.name =名称
}

def addImport(Class importClass){
进口<< $ {importClass.getPackage()。getName()}+
。$ {importClass.getSimpleName()}
}

def addField(String name,Class type){
fields [name] = type.simpleName
}
$ b $ def addMethod(String name,Closure closure){
methods [name] = closure

$ b $ def getCreatedClass(){

def templateText ='''
<%imports.each {%> import $ it \\\
<%}%>
class $ name
{
<%fields.each {%> $ it.value $ it.key \\\
<%}%>

'''
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(日历)

uilder.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()


Given a class name I would like to dynamically create a Groovy class add properties and methods to it. I create the new class using

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

For methods I use

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

where it.key is a string (method name) and it.value is a closure. This is convenient because I can specify method parameter types and get type checking. However, I am not able to specify a dynamically created property type without assigning it a value. I can work around this by explicitly defining getter and setter for the property. This works, but it seems that neither metaClass.name = value nor metaClass.getName = {} actually create a field in the class because the Java field operator doesn't work for the created properties. Can I ad a property to a Groovy class and specify its type without assigning an initial value to it or explicitly defining getter and setter methods? Is there a way to add a new field to a Groovy class? Here is the script:

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')

[Edit]

The use case is like this: I define an interface or a base class in Java. A user implements the interface or extends the base class in Groovy and the class is passed back to Java to be used by the main application. Users are not programmers so they define the class using a simple DSL and I construct the actual class using a builder. I am still experimenting with Groovy/JRuby and Java interop (new to both languages).

解决方案

I have more or less been able to get it working by using GroovyClassLoader and SimpleTemplateEngine. here is the code:

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
    }
}

and here is an example of how I use it to create a class dynamically:

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()

这篇关于动态创建一个Groovy类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆