如何防止Groovy通过访问器方法创建字段?

时间:2018-08-16 02:42:46

标签: groovy

如果我有以下代码:

class Person {
    String getName() {
        "John Doe"
    }

    boolean isMan() {
        true
    }
}

println new Person().properties

然后,它打印:

[class:class Person, man:true, name:John Doe]

如何防止groovy基于访问器方法创建属性(人,名字)?我理解为什么groovy会创建属性,但是希望有一种方法(例如使用一些注释)来防止groovy这样做。

1 个答案:

答案 0 :(得分:0)

您必须注意,new Person().properties不会返回有关类字段的类属性列表,而是返回元类属性。 Groovy使用称为MetaBeanProperty的类来表示-类字段和访问器方法。如果编译Groovy类,您将获得.class文件,该文件将反编译为如下所示:

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;

public class Person implements GroovyObject {
    public Person() {
        CallSite[] var1 = $getCallSiteArray();
        MetaClass var2 = this.$getStaticMetaClass();
        this.metaClass = var2;
    }

    public String getName() {
        CallSite[] var1 = $getCallSiteArray();
        return "John Doe";
    }

    public boolean isMan() {
        CallSite[] var1 = $getCallSiteArray();
        return true;
    }
}

如您所见,没有为您的getter方法创建任何文件,但是由于new Person().propertiesname返回了manMetaBeanProperty属性。

如果仅想获取类字段的映射,则必须过滤new Person().properties返回的映射。 MetaBeanProperty类具有此方法getField(),只要给定的MetaBeanProperty由类字段表示,则返回CachedField,否则返回null。考虑以下示例:

import groovy.transform.CompileStatic
import groovy.transform.TypeChecked

@CompileStatic
@TypeChecked
class Person {

    String email

    String getName() {
        "John Doe"
    }

    boolean isMan() {
        true
    }

    static void main(String[] args) {
        def person = new Person()

        def fields = person.properties.findAll {
            (person.hasProperty(it.key.toString()) instanceof MetaBeanProperty) && ((MetaBeanProperty) person.hasProperty(it.key.toString())).getField() != null
        }

        println fields
    }
}

在此示例中,我们添加了字段email。根据上述规则过滤属性列表,将返回以下映射:

[email:null]

最后但并非最不重要的一点-这是反编译Person文件后经过修改的.class类的样子:

import Person._main_closure1;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.lang.Reference;
import java.util.Map;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;

public class Person implements GroovyObject {
    private String email;

    public Person() {
        MetaClass var1 = this.$getStaticMetaClass();
        this.metaClass = var1;
    }

    public String getName() {
        return "John Doe";
    }

    public boolean isMan() {
        return true;
    }

    public static void main(String... args) {
        Reference person = new Reference(new Person());
        Map fields = DefaultGroovyMethods.findAll(DefaultGroovyMethods.getProperties((Person)person.get()), new _main_closure1(Person.class, Person.class, person));
        DefaultGroovyMethods.println(Person.class, fields);
        Object var10000 = null;
    }

    public String getEmail() {
        return this.email;
    }

    public void setEmail(String var1) {
        this.email = var1;
    }
}

同样,getName()isMan()方法不会创建类字段,具有默认Groovy范围的email字段会转换为具有访问器方法的私有类字段。希望对您有所帮助。

在哪里创建类属性?

如果要查看和/或调试创建new Person().properties地图的位置,请查看MetaClassImpl.getProperties() method in line 2083。这是您的类调用以从元类获取属性列表的方法。如您所见,它接受两种类型的属性-缓存的字段(真实类字段)和meta bean属性(可能由字段或访问器方法表示的属性)。不幸的是,没有选项可以排除从getProperties()方法中获取getter方法。

如果您打算将访问器方法从new Person().properties中排除,则实现此目的的唯一方法是重命名该方法,这样它就不会被视为访问器方法(例如getName()-> {{ 1}})。在这种情况下,Groovy的元类将不会检索此类方法的meta bean属性,并且它返回的值将不会出现在name()映射中。