Динамическое создание метода в Groovy с аргументами

Например, у меня есть требование, когда я использую SuperCSV для чтения файла csv и сопоставления с объектом.

У SuperCSV есть требование, согласно которому, если в заголовке указано имя поля «firstName», он должен иметь метод setFirstName() в классе, иначе он выдает исключение.

Теперь, если я использую компоненты Groovy, мне не нужно объявлять все эти методы, просто объявив, что переменные должны работать для SuperCSV.

Но я искал более динамичное решение, в котором нам даже не нужно объявлять эти переменные.

Например, просто создайте объект на лету, и с помощью функции создания динамических методов Groovy SuperCSV сможет найти сеттеры.

Я просмотрел различные варианты, такие как Expando,ExpandoMetaClass, но они не соответствовали моей цели.

Любой ответ будет оценен.

Вот фрагмент кода, который сработал для меня и может быть действительно полезен для сокращения количества кода и ненужной логики:

    static def testBeanReader()
{
    ICsvBeanReader beanReader = null;
    try 
    {
        beanReader = new CsvBeanReader(new FileReader("src/test.csv"),
                                 CsvPreference.STANDARD_PREFERENCE);                      
    }
    catch(Exception e)      
    {

    }

    final String[] header = VirtualObject.getHeaders();
    final CellProcessor[] processors = VirtualObject.getProcessors();

    //Class c1 = createNewClass()

    //String s = createClass()
    def list = ["name", "age"]
    def c = (new GroovyShell().evaluate(createClass(list)) as Class)
    //println(c.methods.grep {it.name.startsWith("get")})

    GroovyObject groovyObject = (GroovyObject)(beanReader.read(c, header, processors))
    Object[] args = {};
    println(groovyObject.getProperty("name"))

}

     static def createClass(def list)
{
    String classDeclaration = "\nclass Test {\n"
    list.each 
    {
        classDeclaration+="def $it\n"
    } 

    classDeclaration+= """
    }
            return Test.class
                              """
    return  classDeclaration
}

Это для supercsv, но может использоваться для общего объекта Java с небольшими изменениями в синтаксисе.


person sachin jain    schedule 10.01.2014    source источник


Ответы (1)


Разве вы не можете использовать оболочку Groovy для динамического создания этих классов.

def c = (new GroovyShell().evaluate("""
class Test {
    def fileName // list properties here
}
return Test.class
""") as Class)

println(c.methods.grep {it.name.startsWith("set")})

def m = (c as Class).getMethod("setFileName", [Object] as Class[])
// We, and I suppose SuperCSV, can access method via Reflexion API

Это небезопасно, но на лету создаст Java-совместимый класс.

Я не знаком с SuperCSV, но я думаю, что он должен использовать что-то вроде отражения, когда решения Expando или MetaClass будут использовать протокол Groovy Meta-object.

GroovyShell (или GroovyClassLoader) сможет создать обычный класс с работающим Reflexion.

ОТРЕДАКТИРОВАНО: иллюстрирует динамическую природу сгенерированного класса. И что мы сначала создаем исходный код класса (как мы хотим, из информации времени выполнения, из ваших заголовков CSV), а затем используем его.

def createClassDeclaration()
{
    String classDeclaration = "\nclass Test {\n"
    // Here you can use your runtime information, schema, array of fields, getted from any source.
    10.times {
        classDeclaration+="def field$it\n" // declares def field1, field2, ... etc
    }
    classDeclaration += """
    }
    return Test.class
    """
    println(classDeclaration)
    return classDeclaration
}
def compile(String s)
{
    def c = (new GroovyShell().evaluate(s) as Class)
    def m = (c as Class).getMethod("setField1", [Object] as Class[])
    assert m
    println(c.methods.grep {it.name.startsWith("set")})

}

compile(createClassDeclaration())
person Seagull    schedule 10.01.2014
comment
Нам даже не нужно этого делать, если мы создаем поле в тройных кавычках. Если вы используете простые компоненты Groovy, то, просто объявляя переменные, он автоматически получает сеттеры и геттеры. Короче говоря, я ищу решение, в котором нам даже не нужно объявлять эти переменные. - person sachin jain; 11.01.2014
comment
@sachinjain, я могу тебя неправильно понять. Я пытаюсь сказать, что вы можете создать это объявление класса, используя вашу схему на лету. Поля не прописаны руками, их можно сгенерировать автоматически. Я отредактировал ответ, пытаясь внести ясность. - person Seagull; 11.01.2014
comment
эй, Чайка, спасибо, это сработало как по волшебству, пришлось немного побороться, так как я новичок в groovy, но, наконец, понял, если кто-то ищет нечто подобное, я обновил свой исходный пост фрагментом кода. - person sachin jain; 11.01.2014
comment
@Seaagull еще один вопрос, если мне нужно создать класс на лету, который расширяет существующий класс, это также возможно. например: в приведенном выше примере кода можно сделать что-то вроде этого: \nclass Test extends Base {\n. Я попытался это сделать, но получил ошибку, неспособную разрешить класс Base. Эбен, хотя класс существует. Я не знал, как искать что-то подобное, хотя безуспешно пытался. - person sachin jain; 14.01.2014
comment
@Seaagull, пожалуйста, не беспокойтесь, я смог это решить, все, что мне было нужно, это весь путь, и это сработало. - person sachin jain; 14.01.2014