Groovy中的超级类字段访问

时间:2016-03-14 15:36:14

标签: groovy

在Groovy中有一个@运算符,它允许直接字段访问。但是看起来它对超类中声明的字段不起作用。考虑两个 Java (非Groovy)类:

class Entity {
  private Long id;

  Long getId() {
    return id;
  }
}

class User extends Entity {
}

然后在Groovy中调用直接访问

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

最终会出现例外:groovy.lang.MissingFieldException: No such field: id for class User

当我尝试使用标准访问user.id = 1L时,我得到groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: id for class User

是否有任何选项可以访问超类中声明的字段?

4 个答案:

答案 0 :(得分:3)

无法从子类访问私有字段(它们不是继承的)。 尽管Groovy允许您比Java反射更容易访问私有字段,但它仍然无法访问不存在的字段。

答案 1 :(得分:3)

您可能需要将该属性声明为受保护的:

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

这是直接访问字段操作符的修改示例。

答案 2 :(得分:3)

您可以通过常规Java反射进行访问,但我不确定如何使其更加“Groovy”。

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

答案 3 :(得分:0)

让我重新回答这个问题,因为我最近经历了类似的事情。以下是一些想法...

由于测试需要,或许用equals进行比较而不是直接检查/设置字段?

如果替代方案是解封装,则值得考虑的模式。我的意思是,如果不需要在产品代码中触摸这些字段,理想情况下它们也不应该在测试中被触及。

Entity将拥有基于equalshashCode的字段。 Lombok的@EqualsAndHashCode注释可以帮助减少样板。

测试就像expect: new Entity(id) == new Entity(id)一样(假设有这样的构造函数)。

如果不是相同的方式,那么包范围可能会通过适当的包结构(main / src / java)提供更好的封装:

package foo.bar

public class Entity {
    Long id
}

id访问的控制现在更加灵活,独立于测试代码。虽然User extends Entity放置在不同的包中但id无法访问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 } } ,但测试代码可以有一个类访问它,例如(src / test / groovy):

EntityAccess.access

我确信可以通过一些花哨的Groovy AST注释来减少样板。关键是prod代码可以在测试中保持字段隐藏,使用given: access(entity).id = 5的静态导入,可以在没有任何Groovy hax0rs的情况下访问字段:

expect: access(entity).id == 5设置或Entity断言实体的ID。

如果可能的话,我宁愿保持User@Entity(tableName = "device") public class Device { @PrimaryKey(autoGenerate = true) public int device_id; @ColumnInfo(name = "identifier") public String identifier; @ColumnInfo(name = "language") public int language; @ColumnInfo(name = "searchFilter") public int searchFilter; public Device(String identifier, int language, int searchFilter){ this.identifier = identifier; this.language = language; this.searchFilter = searchFilter; } } 不可变并通过相等测试而不改变对象状态。