在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
是否有任何选项可以访问超类中声明的字段?
答案 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
将拥有基于equals
和hashCode
的字段。
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;
}
}
不可变并通过相等测试而不改变对象状态。