簡體   English   中英

動態創建Groovy類

[英]Create a Groovy class dynamically

給定一個類名,我想動態創建一個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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM