Java – dynamically create groovy classes

Given a class name, I want to dynamically create a groovy class and add properties and methods to it I use to create new classes

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

For the method I use

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

Including it Key is a string (method name), it Value is a closure This is convenient because I can specify method parameter types and get type checks However, I cannot specify a dynamically created property type without assigning a value to it I can solve this problem by explicitly defining getters and setters for properties This works, but it seems metaclass Name = value and metaclass Getname = {} does not actually create a field in the class because the Java field operator is not applicable to the created property Can I add a property to a groovy class and specify its type without assigning it an initial value or explicitly defining getter and setter methods? Is there any way to add new fields to groovy classes? This 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 base class in Java The user implements the interface or extension base class in groovy and passes the class back to Java for use by the main application Users are not programmers, so they use simple DSL to define classes and use builders to construct actual classes I'm still trying to use groovy / jruby and Java interop (both languages are new)

Solution

By using groovy classloader and simpletemplateengine, I can make it work more or less This 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
    }
}

Here is an example of how I use it to dynamically create classes:

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()
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>