Java設計模式——工廠方法&模版方法
阿新 • • 發佈:2017-10-05
ctype sys 通過反射 ets gif pla ons view close ,並使用它來創建新的實例。最便利的工廠對象就是Class對象,如下代碼所示:
Java的泛型一直是我比較感興趣的部分,但是既然說起泛型,就不得不提到擦除。Java泛型是使用擦除實現的,使用泛型時,具體的類型信息都被“擦除”了。舉個例子:List<String>和List<Integer>在運行時實際上都是相同的類型,都被擦除成了“原生的”類型,即List。
泛型類型參數將擦除到它的第一個邊界,如List<T>將被擦除成List,而普通的類型變量在未指定邊界的情況下被擦除成Object。
1 package com.test.generic; 2 3 public class Erased<T> { 4View Codeprivate final int SIZE = 100; 5 public static void f(Object arg) { 6 if(arg instanceof T) {} //編譯報錯 7 T var = new T(); //編譯報錯 8 T[] array = new T[SIZE]; //編譯報錯 9 T[] array = new Onject[SIZE]; //編譯報錯 10 } 11 }
工廠方法
在上面的代碼中,創建一個new T()的操作無法實現,部分原因就是擦除,另一部分原因是因為編譯器不能驗證碼T具有默認(無參)的構造器。Java的解決方案是傳遞一個工廠對象
1 package com.test.generic; 2 3 class ClassAsFactory<T> { 4 T x; 5 public ClassAsFactory(Class<T> kind) { 6 try { 7 x = kind.newInstance(); 8 } catch (Exception e) { 9 throw new RuntimeException(e);View Code10 } 11 } 12 } 13 14 class Employee {} 15 16 public class InstantiateGenericType { 17 18 public static void main(String[] args) { 19 ClassAsFactory<Employee> fe = new ClassAsFactory<Employee>(Employee.class); 20 System.out.println("ClassAsFactory<Employee> succeeded"); 21 22 try { 23 ClassAsFactory<Integer> fi = new ClassAsFactory<Integer>(Integer.class); 24 } catch (Exception e) { 25 System.out.println("ClassAsFactory<Employee> failed"); 26 } 27 } 28 29 }
運行結果如下
從運行結果看到,可以編譯,但是會因為ClassAsFactory<integer>而失敗,因為Integer沒有任何默認的構造器。因為這個錯誤不是在編譯期捕獲的。Sun公司建議使用顯式的工廠,並限制類型,只能接受實現了這工廠的類,如下代碼所示:
1 package com.test.generic; 2 3 interface FactoryI<T> { 4 T create(); 5 } 6 7 class Foo2<T> { 8 private T x; 9 public <F extends FactoryI<T>> Foo2(F factory) { 10 x = factory.create(); 11 } 12 } 13 14 class IntegerFactory implements FactoryI<Integer> { 15 public Integer create() { 16 return new Integer(0); 17 } 18 } 19 20 class Widget { 21 public static class Factory implements FactoryI<Widget> { 22 @Override 23 public Widget create() { 24 return new Widget(); 25 } 26 } 27 } 28 public class FactoryConstraint { 29 public static void main(String[] args) { 30 new Foo2<Integer>(new IntegerFactory()); 31 new Foo2<Widget>(new Widget.Factory()); 32 } 33 }View Code
Class<T>碰巧是創建內建的工廠對象,而通過上面那樣顯示的創建一個工廠對象,可以獲得編譯期的檢查。
模版方法:
另一種方法是使用模版方法設計模式,通過實現抽象類(模版)的抽象方法來創建對象,同樣能獲得編譯期的類型檢查,下面是代碼:
1 package com.test.generic; 2 3 abstract class GenericWithCreate<T> { 4 final T element; 5 GenericWithCreate() { 6 element = create(); 7 } 8 abstract T create(); 9 } 10 11 class X {} 12 13 class Creator extends GenericWithCreate<X> { 14 @Override 15 X create() { 16 return new X(); 17 } 18 void f() { 19 System.out.println(element.getClass().getSimpleName()); 20 } 21 } 22 public class CreatorGeneric { 23 public static void main(String[] args) { 24 Creator c = new Creator(); 25 c.f(); 26 } 27 }View Code
總結:
在使用泛型的過程中,因為擦除的存在,任何在運行時需要知道確切類型信息的操作都沒法好好工作。通過反射配合工廠方法或者模版方法的使用,我們能在編譯器就知道創建對象時傳遞的參數類型是否正確。
Java設計模式——工廠方法&模版方法