java類型異構容器,可以存儲任何對象類型為其他類提供該對象 ...
最近在著手重構一個java UI桌面項目,發現這個項目在一開始的時候由於需求不明確,以及開發人員對swing框架不熟悉等問題造成了頁面代碼混亂的情況:為了能夠在各個類里都可以拿到其他類的引用去進行相應的頁面響應操作,在每一個類的構造方法中都傳入了主類的引用,在主類中提供了所有類的get()方法,這樣的做法顯得十分的臃腫,就像這樣:
打開主頁面後會顯示窗體B,窗體B的按鈕支持我們打開窗體A,窗體A按鈕支持修改B中屬性.我們只能通過在主頁面的類中使用get(),set()方法來持有A和B的引用,在A和B的構造方法中提供主頁面的引用,從而才能做到在B中調用A,A中調用B.但是這樣的做法隨著項目的開展主頁面的類中get()和set()方法的數量將多到你無法想象,那麼是否可以提供一個容器,在創建頁面時就將該頁面對象存入該容器中,其他頁面只需通過這個容器來獲取其他頁面窗體進行操作?
廢話不多說我們開始幹活,由於不同的頁面類型即其類可能不同所以我們提供的容器需要是Object的:
1 public class ClassContainerOne { 2 private static Map<String, Object> container = new HashMap<>(); 3 4 public static void addClass(String name,Object value){ 5 container.put(name,value); 6 } 7 public static Object getClass(String name){ 8 return container.get(name); 9 } 10 }
簡單的封裝一下我們就可以正常使用,這樣的操作,我們只能依靠String來區分對象並且自己來完成強制類型轉換:
1 public class Test { 2 public static void main(String[] args) { 3 Teacher teacher = new Teacher("A老師"); 4 Student student = new Student("B學生"); 5 ClassContainerOne.addClass("teacher",teacher); 6 ClassContainerOne.addClass("student",student); 7 Teacher teacher1 = (Teacher) ClassContainerOne.getClass("teacher"); 8 Student student1 = (Student)ClassContainerOne.getClass("student"); 9 System.out.println(teacher1 + " " + student1); 10 } 11 }
我想大家都會想到一個問題,那就是這樣的操作安全嗎?顯然是否定的,一旦我們強制轉換錯誤,那系統就會崩潰,因此我們用泛型來修改完善我們的容器類:
1 public class ClassContainerTwo { 2 private static Map<Class<?>, Object> container = new HashMap<>(); 3 4 public static <T> void addClass(Class<T> valueType,T value) { 5 container.put(valueType, value); 6 } 7 8 public static <T> T getClass(Class<T> valueType) { 9 return valueType.cast(container.get(valueType)); 10 } 11 }
我們轉為使用其Class類型作為key值來對應我們的對象,確實可以做到對象獲取時的萬無一失:
1 public class Test { 2 public static void main(String[] args) { 3 Teacher teacher = new Teacher("A老師"); 4 Student student = new Student("B學生"); 5 ClassContainerTwo.addClass(Teacher.class,teacher); 6 ClassContainerTwo.addClass(Student.class,student); 7 Teacher teacher1 = ClassContainerTwo.getClass(Teacher.class); 8 Student student1 = ClassContainerTwo.getClass(Student.class); 9 System.out.println(teacher1 + " " + student1); 10 } 11 }
但是這樣做的代價就是我們無法存放多個相同的對象,我們可以創建一個鉤子類來銜接這個類容器和各個對象:
1 public class Key<T> { 2 private String name; 3 private Class<T> valueType; 4 5 public Key(String name, Class<T> valueType) { 6 this.name = name; 7 this.valueType = valueType; 8 } 9 10 /** 11 * 同時重寫equals()和hashCode(),避免加入類容器是和 12 * 從類容器中取出對象時實例化的key不是同一個對象,及類屬性相同,但是地址不同 13 */ 14 @Override 15 public boolean equals(Object o) { 16 if (this == o) return true; 17 if (o == null || getClass() != o.getClass()) return false; 18 Key<?> key = (Key<?>) o; 19 return Objects.equals(name, key.name) && 20 Objects.equals(valueType, key.valueType); 21 } 22 23 @Override 24 public int hashCode() { 25 return Objects.hash(name, valueType); 26 } 27 28 public Class<T> getValueType() { 29 return valueType; 30 } 31 }
然後繼續完善我們的類容器:
1 public class ClassContainerThree { 2 private static Map<Key<?>,Object> container = new HashMap<>(); 3 4 public static <T> void addClass(Key<T> key,T value) { 5 container.put(key, value); 6 } 7 8 public static <T> T getClass(Key<T> key) { 9 return key.getValueType().cast(container.get(key)); 10 } 11 }
這樣的封裝,雖然對於Key的實例化代碼較長,但是很好的解決了我們的類容器存儲和獲取問題:
1 public class Test { 2 public static void main(String[] args) { 3 Teacher teacher = new Teacher("A老師"); 4 Student student = new Student("B學生"); 5 ClassContainerThree.addClass(new Key<>("teacher",Teacher.class),teacher); 6 ClassContainerThree.addClass(new Key<>("teacher",Student.class),student); 7 Teacher teacher1 = ClassContainerThree.getClass(new Key<>("teacher",Teacher.class)); 8 Student student1 = ClassContainerThree.getClass(new Key<>("teacher",Student.class)); 9 System.out.println(teacher1 + " " + student1); 10 } 11 }