前言
當構造一個複雜耗時的對象的時候,而且實際用的時候僅僅只是需要改動一小部分,這個時候我們往往會想到使用實現Cloneable接口來開啓對象克隆。但是這裏有一個問題就是,對於自定義對象無法支持深拷貝,HashMap實現了Cloneable接口,自然也是存在這種問題的。
示例
- 示例代碼
public class Test3 {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
HashMap<String, Person> map = new HashMap<>();
Person person = new Person();
person.setName("小明");
map.put("xiaoming", person);
HashMap<String, Person> map1 = (HashMap<String, Person>) map.clone();
Person person1 = map1.get("xiaoming");
System.out.println(person);
System.out.println(person1);
}
}
- 結果
cn.itoak.storm.clone.Person@4dd8dc3
cn.itoak.storm.clone.Person@4dd8dc3
- 說明
在上面的示例中,Map的value是自定義類Person,在map.clone()後,我們看到person和person1的內存地址是一樣的,這樣的拷貝,在很多時候是會存在問題的,屬於淺拷貝。
解決方案
-
思路
通過重寫HashMap的clone(),實現value的拷貝,這種做法就相當於我們自己去增強了HashMap的clone函數。由於實現Cloneable接口非必須重寫clone()函數,所有我們得定義RequiredCloneable接口繼承Cloneable接口,讓自定義類去實現RequiredCloneable接口 -
方案代碼
核心:RequiredCloneable接口
public interface RequiredCloneable extends Cloneable {
Object clone();
}
核心:自定義DeepCloneHashMap
public class DeepCloneHashMap<K, V extends RequiredCloneable> extends HashMap<K, V> {
@Override
@SuppressWarnings("unchecked")
public DeepCloneHashMap<K, V> clone() {
DeepCloneHashMap<K, V> deepCloneHashMap = (DeepCloneHashMap<K, V>) super.clone();
this.forEach((key, value) -> deepCloneHashMap.put(key, (V) value.clone()));
return deepCloneHashMap;
}
}
以上兩段代碼可直接使用,需要注意的地方:
(1)V extends RequiredCloneable,限制V的類型;
(2)value.clone(),這個就是爲什麼需要使用RequiredCloneable接口的原因
自定義Person類
public class Person implements RequiredCloneable {
public String name;
@Override
public Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
- 使用示例
public class Test {
public static void main(String[] args) {
DeepCloneHashMap<String, Person> map = new DeepCloneHashMap<>();
Person person = new Person();
person.setName("小明");
map.put("xiaoming", person);
DeepCloneHashMap<String, Person> map1 = map.clone();
Person person1 = map1.get("xiaoming");
System.out.println(person);
System.out.println(person1);
}
}
- 結果
cn.itoak.storm.clone.Person@46f7f36a
cn.itoak.storm.clone.Person@421faab1
最後
以上是筆者在工作中發現的問題,以及最後的解決方案,以上方案不會處理更深層次的拷貝,意思也就是說不會拷貝Person類中自定義的類,由於筆者的問題不涉及更深次的拷貝,因而也沒有繼續處理,這裏僅僅是拋磚引玉,提供一種解決思路吧。