方案
- 通过反射找到对应的字段
- 修改
modifiers为非final
- 设置值
代码(groovy4)
/**设置字段值,如果是final的会修改为非final再设值,会查找父类的私有字段进行设值
* @objOrClz 对象(设置属性传对象)或类(设置静态变量传Class)
* @fieldName 字段名
* @newValue 新值
* */
static void setObjFieldVal(Object objOrClz, String fieldName, Object newValue) throws Exception {
Class clz
if (objOrClz instanceof Class){
clz=objOrClz
}else{
clz=objOrClz.getClass()
}
def tmp = clz
boolean hasDone = false
while (tmp!=Object.class&&clz!=GroovyObject.class&&tmp!=null){
Field field
try {
field = tmp.getDeclaredField(fieldName)
}catch (Exception ignored){
tmp=tmp.superclass
continue
}
field.accessible=true
if (Modifier.isFinal(field.getModifiers())){
def modifiersField=field.class.getDeclaredField('modifiers')
modifiersField.accessible=true
//修改为非final,这只对Field反射生效,不对直接访问生效
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL)
}
def isS = Modifier.isStatic(field.getModifiers())
def t= field.type
if (t.isPrimitive()){
//基础数据类型需要转换
switch (t){
case boolean:
field.set(isS?null:objOrClz, newValue==null?false:(newValue as boolean))
break
case char:
field.set(isS?null:objOrClz,newValue==null?(char)0:(newValue as char))
break
case byte:
field.set(isS?null:objOrClz,newValue==null?(byte)0:(newValue as byte))
break
case int:
field.set(isS?null:objOrClz,newValue==null?0:(newValue as int))
break
case long:
field.set(isS?null:objOrClz,newValue==null?0L:(newValue as long))
break
case float:
field.set(isS?null:objOrClz,newValue==null?0F:(newValue as float))
break
case short:
field.set(isS?null:objOrClz,newValue==null?(short)0:(newValue as short))
break
case double:
field.set(isS?null:objOrClz,newValue==null?0D:(newValue as double))
break
default:
throw new RuntimeException(
"unsupported primitive type:${t.name} in class ${clz.name} var ${fieldName}")
}
}else{
field.set(isS?null:objOrClz, newValue)
}
//如果有必要把set包起来在finally中设置回final:(modifiersField.setInt(field,field.getModifiers()&Modifier.FINAL))
hasDone=true
if (!Modifier.isPrivate(field.getModifiers())||isS){
//静态变量及非私有属性不再向上查找父类
break
}else{
tmp=tmp.superclass
}
}
if (!hasDone){
throw new NoSuchFieldException("class ${clz.name} does not have var ${fieldName}!")
}
}