如何使用反射使 Java Bean 的可变字段在 运行 时间不可变
How to make mutable fields of a Java Bean immutable at run time using reflection
我正在从库中获取一些 Java 对象,这些对象将可变地图对象作为字段。我需要一种方法,通过将地图对象包装在 Collections.unmodifiableMap() 方法中,使这些对象字段只在 运行 时间读取。我已经使用 Java 反射 API 尝试了以下方法,但我仍然无法从现场获取实际的地图实例:
public static <T>T freeze(T obj){
if(obj!=null){
Field[] fields=obj.getClass().getDeclaredFields();
for(Field field:fields){
if(field.getType().equals(Map.class)){
//How to wrap a Map instance from the field in Collections.unmodifiableMap() object
}
}
}
return obj;
}
编辑-------------------------------- ---------------------------------------------- --------------------------------------
我写了一个 Immutable Date class 它将包装一个 java.util.Date 对象并将禁用所有可变操作。使用这个包装器我可以获得类似于 Collections.unmodifiableCollection().
的功能
final class DateUtil{
private DateUtil(){}
/**
*
* @param date
* @return Date
*
* This method will return an unmodifiable Date object.
*
*/
public static Date unmodifiableDate(Date date){
if(date==null){
throw new IllegalArgumentException();
}
return new ImmutableDate(new Date(date.getTime()));
}
/**
* This Date sub-class will override all the mutable Date operations
* and throw UnsupportedOperationException.
*/
private final static class ImmutableDate extends Date{
private static final long serialVersionUID = -1869574656923004097L;
private final Date date;
ImmutableDate(Date date){
this.date=date;
}
@Override
public void setTime(long date) {
throw new UnsupportedOperationException();
}
@Override
public void setDate(int date) {
throw new UnsupportedOperationException();
}
@Override
public void setHours(int hours) {
throw new UnsupportedOperationException();
}
@Override
public void setMinutes(int minutes) {
throw new UnsupportedOperationException();
}
@Override
public void setSeconds(int seconds) {
throw new UnsupportedOperationException();
}
@Override
public void setYear(int year) {
throw new UnsupportedOperationException();
}
@Override
public void setMonth(int month) {
throw new UnsupportedOperationException();
}
@Override
public Object clone() {
throw new UnsupportedOperationException();
}
}
}
一种可能的解决方案是从要冻结的对象中获取映射值,将其转换为 Collections.unmodifiableMap() ,然后将值设置为对象。在字段操作之前,您应该设置 field.setAccessible(true)
。
public static <T>T freeze(T obj) throws IllegalAccessException {
if(obj!=null){
Field[] fields=obj.getClass().getDeclaredFields();
for(Field field:fields){
//update accessibility
field.setAccessible(true);
if(field.getType().equals(Map.class)){
//Convert the map field to Collections.unmodifiableMap() object and update the field
field.set(obj, Collections.unmodifiableMap((Map<?, ?>) field.get(obj)));
}
}
}
return obj;
}
我正在从库中获取一些 Java 对象,这些对象将可变地图对象作为字段。我需要一种方法,通过将地图对象包装在 Collections.unmodifiableMap() 方法中,使这些对象字段只在 运行 时间读取。我已经使用 Java 反射 API 尝试了以下方法,但我仍然无法从现场获取实际的地图实例:
public static <T>T freeze(T obj){
if(obj!=null){
Field[] fields=obj.getClass().getDeclaredFields();
for(Field field:fields){
if(field.getType().equals(Map.class)){
//How to wrap a Map instance from the field in Collections.unmodifiableMap() object
}
}
}
return obj;
}
编辑-------------------------------- ---------------------------------------------- -------------------------------------- 我写了一个 Immutable Date class 它将包装一个 java.util.Date 对象并将禁用所有可变操作。使用这个包装器我可以获得类似于 Collections.unmodifiableCollection().
的功能final class DateUtil{
private DateUtil(){}
/**
*
* @param date
* @return Date
*
* This method will return an unmodifiable Date object.
*
*/
public static Date unmodifiableDate(Date date){
if(date==null){
throw new IllegalArgumentException();
}
return new ImmutableDate(new Date(date.getTime()));
}
/**
* This Date sub-class will override all the mutable Date operations
* and throw UnsupportedOperationException.
*/
private final static class ImmutableDate extends Date{
private static final long serialVersionUID = -1869574656923004097L;
private final Date date;
ImmutableDate(Date date){
this.date=date;
}
@Override
public void setTime(long date) {
throw new UnsupportedOperationException();
}
@Override
public void setDate(int date) {
throw new UnsupportedOperationException();
}
@Override
public void setHours(int hours) {
throw new UnsupportedOperationException();
}
@Override
public void setMinutes(int minutes) {
throw new UnsupportedOperationException();
}
@Override
public void setSeconds(int seconds) {
throw new UnsupportedOperationException();
}
@Override
public void setYear(int year) {
throw new UnsupportedOperationException();
}
@Override
public void setMonth(int month) {
throw new UnsupportedOperationException();
}
@Override
public Object clone() {
throw new UnsupportedOperationException();
}
}
}
一种可能的解决方案是从要冻结的对象中获取映射值,将其转换为 Collections.unmodifiableMap() ,然后将值设置为对象。在字段操作之前,您应该设置 field.setAccessible(true)
。
public static <T>T freeze(T obj) throws IllegalAccessException {
if(obj!=null){
Field[] fields=obj.getClass().getDeclaredFields();
for(Field field:fields){
//update accessibility
field.setAccessible(true);
if(field.getType().equals(Map.class)){
//Convert the map field to Collections.unmodifiableMap() object and update the field
field.set(obj, Collections.unmodifiableMap((Map<?, ?>) field.get(obj)));
}
}
}
return obj;
}