简体   繁体   English

Groovy中的超级类的现场访问

[英]Field access in Groovy for super class

In Groovy there is an @ operator which enables direct field access. 在Groovy中,有一个@运算符可用于直接字段访问。 However it looks like it won't work for fields declared in super class. 但是,它似乎不适用于在超类中声明的字段。 Consider two Java (not Groovy) classes: 考虑两个Java (不是Groovy)类:

class Entity {
  private Long id;

  Long getId() {
    return id;
  }
}

class User extends Entity {
}

Then invoking direct access in Groovy 然后在Groovy中调用直接访问

User user = new User();
user.@id = 1L

ends up with exception: groovy.lang.MissingFieldException: No such field: id for class User 最终出现异常: groovy.lang.MissingFieldException: No such field: id for class User

When I try to use standard access user.id = 1L I get groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: id for class User 当我尝试使用标准访问user.id = 1L groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: id for class User

Is there any option to access field declared in super class? 是否可以访问超类中声明的字段?

You would probably need to declare the property as protected instead: 您可能需要将属性声明为protected:

class Entity {
  protected Long id;

  Long getId() {
    return id * 2;
  }
}

class User extends Entity {
}

User user = new User();
user.@id = 1L

assert user.@id == 1L
assert user.id == 2L

This is a modified example for the direct access field operator. 这是直接访问字段操作员的修改示例。

You can access via regular Java reflection but I'm not sure how to make this more "Groovy". 您可以通过常规Java反射进行访问,但是我不确定如何使它更“时髦”。

User user = new User()
fields = user.getClass().superclass.declaredFields
idField = fields[0]
idField.accessible = true
idField.set(user, 2L)
println idField.get(user)

Private fields can't be accessed from the children classes (they aren't inherited). 不能从子类访问私有字段(它们不会被继承)。 Although Groovy lets you access private fields easier than Java reflection, it still can't access fields which aren't exist. 尽管Groovy使您可以比Java反射更轻松地访问私有字段,但是它仍然无法访问不存在的字段。

Let me revive this question as I'm going through a similar thing recently. 让我复活这个问题,因为我最近正在经历类似的事情。 Here are a few thoughts... 这里有一些想法...

As it's needed for tests perhaps a comparison by equals would do instead of checking/setting fields directly? 由于测试需要,也许用等值比较代替直接检查/设置字段?

Pattern worth considering if the alternative is de-encapsulation. 如果替代方法是解封装,则值得考虑的模式。 I mean, if there is no need to touch these fields in prod code ideally they shouldn't need being touched in tests either. 我的意思是,如果理想情况下无需触摸产品代码中的这些字段,那么它们也无需在测试中被触及。

Entity would have own fields based equals and hashCode implemented. Entity将具有基于equals自己的字段并实现hashCode Lombok's @EqualsAndHashCode annotation can help with reducing the boilerplate. 龙目岛的@EqualsAndHashCode注释可以帮助减少样板。

Test goes then like expect: new Entity(id) == new Entity(id) (assuming there is such constructor). 然后进行测试,就像expect: new Entity(id) == new Entity(id) (假设有这样的构造函数)。

If equality is not the way to go package scope might give better encapsulation with an appropriate package structure (main/src/java): 如果不是平等,包范围可以通过适当的包结构(main / src / java)更好地封装:

package foo.bar

public class Entity {
    Long id
}

Control over the id access is more flexible now and independent from the test code. 现在,对id访问的控制更加灵活,并且独立于测试代码。 While User extends Entity can have no access to the id if placed in a different package, the test code can have a class accessing it, like for example (src/test/groovy): 如果将User extends Entity放置在不同的程序包中,则它不能访问该id ,但是测试代码可以具有一个访问它的类,例如(src / test / groovy):

package foo.bar

class EntityAccess {
    private final Entity entity

    EntityAccess(Entity entity) {
        this.entity = entity
    }

    static EntityAccess access(Entity entity) {
        new EntityAccess(entity)
    }

    Long getId() {
        entity.id
    }

    void setId(Long id) {
        entity.id = id
    }
}

I'm sure the boilerplate can be reduced with some fancy Groovy AST annotations. 我确信可以通过一些时髦的Groovy AST注释来减少样板。 The point is prod code can keep the fields hidden while in tests, with a static import for EntityAccess.access , a field can be accessed without any Groovy hax0rs: 关键是产品代码可以在测试期间隐藏字段,对于EntityAccess.access可以使用静态导入进行访问,而无需任何Groovy hax0rs即可访问该字段:

given: access(entity).id = 5 to set or expect: access(entity).id == 5 to assert on entity's id. given: access(entity).id = 5来设置或expect: access(entity).id == 5来声明实体的id。

If possible though, I would've rather kept both the Entity and User immutable and tested by equality without altering the objects state. 如果可能的话,我宁愿保持EntityUser不变,并通过相等性进行测试而不更改对象状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM