java的纯函数方法其实就是要大量使用深/浅copy方法的意思吧, 这样会造成性能问题吗?有何建议?

  1. pojo非基本类型field的setter/getter, sonar这类质量监测工具都是认为这样的代码存在潜在的问题

class User{
    private String name;
    private List<String> hobbies;//爱好
    private Address address;//住址
}

上面的hobbies以及address的setter/getter就需要改进, 不能使用IDE自动生成的形式, 而是需要手动clone, 来防止外界对象的更改导致User内部的field也跟着更改

  1. 通用设置对象的创建人/时间的方法

public T assembleCommonInfo(T t){
    T newT = t.someCloneMethod();//将t复制成newT
    
    //设置当前操作用户的userid
    newT.setCreateBy(SessionContext.getCurrentUser().getUserId());
    
    //设置当前操作事件
    newT.setCreateTime(DateKit.now());
    
    return newT;
}

以上拼装了要save到db中对象创建信息, 复制一个新的对象拼装后返回, 这样不影响原对象t的自身field状态

问题1: 我知道这样一定会减少不可预见bug的产生, 但是这样对性能是否有不好的影响, 付出编码的代价是否值得?
问题2: 大家一般使用的copy库是那个, spring自带的, 还是apache common中自带的, 或是还有没有其他更好的
问题2补充: 要复制的2个对象之间的属性名如果不一样, 有没有好的方法

阅读 2.4k
1 个回答

1:如果clone的层次比较深,自己实现clone方法很是麻烦,性能还是可以接受的,如果clone层次很深,编码的代价有点不值得。每个被包含的对象,不管是直接包含,还是间接包含都要实现clone方法。
2:对象的拷贝,spring自带的和apache common的没有使用过,我实际开发中,如果对象需要深拷贝,一般使用序列化实现拷贝,JDK内置的序列化,也即在需要序列化的类上implements Serializable接口去实现序列化,我想说的是这种方式在小系统中尚且可以用一用,如果是并发很大的系统会受到严重影响,这是由于JDK自带的序列化效率很低,不论是时间上还是空间上。这里我推荐我现在公司使用的protostuff,效率相当高。
参考代码:

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
public class SerializeUtils {
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 4);
        try {
            Schema<T> schema = RuntimeSchema.getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }
    
    public static <T> T deserialize(byte[] data, Class<T> cls) {
        try {
            Schema<T> schema = RuntimeSchema.getSchema(cls);
            T message = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(data, message,schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
}

获取对象的拷贝:无非就是一个先序列化然后在反序列化的过程,参考代码:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Person source = new Person();
        source.setName("lisi");
        List<Address> addressList = new ArrayList<Address>();
        addressList.add(new Address("鼓楼","111"));
        source.setAddressList(addressList);
        Person dest = deepClone(source);
        System.out.println(dest == source);//false
        System.out.println(dest.getAddressList() == source.getAddressList());//false
        System.out.println(dest.getAddressList().get(0) == source.getAddressList().get(0));//false
        System.out.println(dest.getName());//lisi
    }
    
    @SuppressWarnings("unchecked")
    public static <T> T deepClone(T t){
        byte[] b = SerializeUtils.serialize(t);
        Class<T> clz = (Class<T>) t.getClass();
        T newT = SerializeUtils.deserialize(b,clz);
        return newT;
    }
   }


class Person { 
     
        private String name;  
        
        private List<Address> addressList;
        
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public List<Address> getAddressList() {
            return addressList;
        }
        public void setAddressList(List<Address> addressList) {
            this.addressList = addressList;
        }
    }  

class Address{

    private String name;
    
    private String iphone;
    
    public Address (String name,String iphone){
        this.name = name;
        this.iphone = iphone;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getIphone() {
        return iphone;
    }
    public void setIphone(String iphone) {
        this.iphone = iphone;
    }
}

3:没有什么好的办法吧,一般都要自己写转换的代码,即使在很多copy库中,你也有实现一个类似convert的接口,实现自己的转化逻辑。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题