如何将Java子类对象克隆为其父类对象?
先帮你把场景捋明白哈:你的JSF应用里,Settings类原本是@ApplicationScoped单例,现在改成@SessionScoped来支持不同用户的个性化配置。但现在有个在独立线程里跑的方法,需要用到Settings的父类对象——之前单例的时候直接传Settings实例就行,现在因为是会话级对象,直接在线程里用不仅有并发风险,还得拿到父类类型的独立实例,对吧?
下面给你几个Java里实用的实现方案:
方案一:手动属性复制(最可控)
这是最稳妥的方式,自己写代码把子类对象的父类属性逐一复制到新的父类实例里。好处是完全掌控复制过程,不会带子类特有的冗余属性。比如:// 假设父类是BaseSettings public static BaseSettings copyToBase(Settings settings) { BaseSettings baseInstance = new BaseSettings(); baseInstance.setConfigKey(settings.getConfigKey()); baseInstance.setTimeout(settings.getTimeout()); // 把父类定义的所有属性都逐一复制 return baseInstance; }方案二:序列化反序列化实现深克隆
如果父类和子类都实现了Serializable接口,可以通过序列化转一圈生成独立的父类实例。注意要确保所有涉及的属性都支持序列化哦。示例代码:public BaseSettings cloneBySerialization(Settings settings) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(settings); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); // 反序列化后强转为父类类型即可 return (BaseSettings) ois.readObject(); }这种方式不用手动写属性复制,但要处理IO和类找不到的异常,而且静态属性、transient修饰的属性不会被复制。
方案三:利用Cloneable接口实现克隆
让父类实现Cloneable接口,重写clone()方法并修改返回类型为父类,子类就可以调用父类的克隆方法生成父类实例。比如:public class BaseSettings implements Cloneable { @Override public BaseSettings clone() throws CloneNotSupportedException { // 默认是浅克隆,如果有引用类型属性,这里要手动处理深克隆 return (BaseSettings) super.clone(); } // 父类的属性和方法 } public class Settings extends BaseSettings { // 子类特有的属性和方法 }使用的时候直接调用:
Settings sessionSettings = ...; // 从会话中获取的实例 BaseSettings clonedBase = sessionSettings.clone();这里要注意,默认的
clone()是浅克隆,如果父类里有引用类型的属性,得手动在clone()方法里把这些属性也克隆一遍,不然新实例和原实例会共享引用对象,线程里用还是有风险。
最后再提醒一句:现在Settings是@SessionScoped,绝对不能直接把原实例传到线程里!会话作用域的对象绑定当前请求线程,跨线程访问不仅有并发问题,还可能拿到错误的用户配置,一定要生成独立的父类实例再传进去。
备注:内容来源于stack exchange,提问作者Kevin Rahe

