Java SE5以前是使用Object來實現其泛型的:java
public class Test { private Object o; public void set(Object o) { this.o = o; } public Object get() { return this.o; } public static void main(String[] args) { Test t = new Test(); t.set("hello world"); System.out.println(t.get()); t.set(12.34); System.out.println(t.get()); } }
但這種實現方式存在兩個的問題:ios
1. 因爲存儲的是Object類型, 則實際調用時候須要強制轉換回原類型. 如執行t.get()操做時候, 實際上將對象o強制轉換回String/Double類型.數組
2. 對於set方法, 咱們能夠寫入任何對象. 但若是這個方法的本來含義只但願存儲類A及其子類的話, 則代碼層面就沒法處理這種狀況.dom
後面引入了新的泛型寫法:ide
public class Test<T> { private T o; public void set(T o) { this.o = o; } public T get() { return this.o; } public static void main(String[] args) { Test<String> t = new Test<>(); t.set("hello world"); System.out.println(t.get()); } }
這裏, 因爲<String>的存在, 完美解決了上述的兩個問題.函數
一個元祖類庫工具
存在以下的需求: 一個方法返回多個對象. 針對return只返回一個對象的狀況, 咱們能夠新建一個泛型類, 存儲多個對象. 而後返回這個類對象.ui
public class TwoTuple { public final A first; public final B second; public TwoTuple(A a, B b) { first = a; second = b; } public String toString() { return "(" + first + "," + second + ")"; } }
一個堆棧類this
public class LinkedStack<T> { private static class Node<U> { U item; Node<U> next; Node() { item = null; next = null; } Node(U item, Node<U> next) { this.item = item; this.next = next; } boolean end() { return item == null && next == null; } } private Node<T> top = new Node<T>(); public void push(T item) { top = new Node<T>(item, top); } public T pop() { T result = top.item; if (!top.end()) { top = top.next; } return result; } public static void main(String[] args) { LinkedStack<String> lss = new LinkedStack<>(); for (String s: "hello world Java".split(" ")) { lss.push(s); } String s; while ((s = lss.pop()) != null) { System.out.println(s); } } }
RandomList編碼
假設咱們須要一個持有特定對象的列表, 每次調用其上的select()方法時, 就隨機選取一個元素.
import java.util.*; public class RandomList<T> { private ArrayList<T> storage = new ArrayList<>(); private Random rand = new Random(47); public void add(T item) { storage.add(item); } public T select() { return storage.get(rand.nextInt(storage.size())); } public static void main(String[] args) { RandomList<String> rs = new RandomList<>(); for (String s: " 1 2 3 4 5 6 7 8 9".split(" ")) { rs.add(s); } for (int i = 0; i < 11; i++) { System.out.print(rs.select() + " "); } } }
假設咱們要編寫一個生成器接口, 那麼須要作以下的準備:
1. 繼承接口Iterable, 它的主要做用是告訴for循環, 下例中的Generator具備循環的能力. 須要實現的接口爲iterator(), 當在程序中執行如for (Integer i: new Generator(5))的時候, 其實是對iterator()方法所返回的對象進行循環.
2. 而iterator()方法返回的對象必須具備可迭代能力, 因此它須要繼承Iterator接口. Iterator接口須要實現的三個方法: next()/hasNext()/remove()方法.
import java.util.*; public class Generator implements Iterable<Integer> { private int[] list = {1, 2, 3, 4, 5, 6}; private int size; private Random rand = new Random(47); Generator() {} Generator(int size) { this.size = size; } public Integer next() { try { size--; return list[rand.nextInt(list.length)]; } catch(Exception e) { throw new RuntimeException(e); } } class GeneratorIterator implements Iterator<Integer> { int count = size; public boolean hasNext() { return count > 0; } public Integer next() { count--; return Generator.this.next(); } public void remove() { throw new UnsupportedOperationException(); } } public Iterator<Integer> iterator() { return new GeneratorIterator(); } public static void main(String[] args) { Generator gen = new Generator(); for (int i = 0; i < 5; i++) { System.out.print(gen.next() + " "); } System.out.println(); for (Integer i: new Generator(5)) { System.out.print(i + " "); } } }
而下例是一個Fibonacci函數的可迭代實現:
import java.util.*; public class IterableFibonacci implements Iterable<Integer>{ private int count = 0; private int size; public IterableFibonacci() {} public IterableFibonacci(int size) { this.size = size; } public Integer next() { return fib(count++); } private int fib(int n) { if (n < 2) return 1; return fib(n - 1) + fib(n - 2); } public Iterator<Integer> iterator() { return new Iterator<Integer>() { public boolean hasNext() { return size > 0; } public Integer next() { size--; return IterableFibonacci.this.next(); } public void remove() { throw new UnsupportedOperationException(); } }; } public static void main(String[] args) { IterableFibonacci f = new IterableFibonacci(); for (int i = 0; i < 18; i++) { System.out.print(f.next() + " "); } System.out.println(); for (int i: new IterableFibonacci(18)) { System.out.print(i + " "); } System.out.println(); } }
對於類的方法來講, 若是能泛型, 則儘可能泛型化.
public class GenericMethods { public <T> void f(T x) { System.out.println(x.getClass().getName()); } public static void main(String[] args) { GenericMethods gm = new GenericMethods(); gm.f(""); gm.f(1); gm.f(1.234); } }
賦值可推斷其泛型類型
當存儲賦值語句時, 右邊的表達式可經過左邊的表達式推斷出其泛型類型. 因此通常代碼:
ArrayList<String> lst = new ArrayList<String>();
可簡化爲:
ArrayList<String> lst = new ArrayList<>();
顯式的類型說明
要顯式的指明類型, 必須在點操做符與方法名之間插入尖括號, 而後把類型置於尖括號內. 若是是在定義該方法的類的內部, 必須在點操做符以前使用this關鍵字, 若是是使用static的方法, 必須在點操做符以前加上類名.
public class Test { private String x = "hello"; public <T> void f(T x) { System.out.println(x.getClass().getName()); } public void show() { this.<String>f(x); } public static <T> void func(T x) { System.out.println(x.getClass().getName()); } public static void main(String[] args) { Test t = new Test(); t.<String>f("hello world"); t.show(); Test.<String>func("hello"); } }
實際上都不須要進行顯式的說明, 編譯器會自動根據具體的數據類型推斷出T的具體類型.
可變參數與泛型方法
可變參數能夠應用於泛型方法之上:
import java.util.*; public class Test { public static <T> List<T> makelist(T... args) { List<T> result = new ArrayList<>(); for (T item: args) { result.add(item); } return result; } public static void main(String[] args) { List<String> lst = makelist("ABCDEFGHI".split("")); System.out.println(lst); } }
用於Generator的泛型方法
import java.util.*; interface Generator<T> { T next(); } class IteratorGenerator implements Generator<Integer>, Iterable<Integer> { private int count = 0; private int size; IteratorGenerator() {} IteratorGenerator(int s) { size = s; } private int fib(int n) { if (n < 2) return 1; return fib(n - 1) + fib(n - 2); } public Integer next() { return fib(count++); } class GeneratorIter implements Iterator<Integer> { private int count = size; public boolean hasNext() { return size > 0; } public Integer next() { size--; return IteratorGenerator.this.next(); } public void remove() { throw new UnsupportedOperationException(); } } public Iterator<Integer> iterator() { return new GeneratorIter(); } public static void main(String[] args) { IteratorGenerator gen = new IteratorGenerator(); for (int i = 0; i < 18; i++) { System.out.print(gen.next() + " "); } System.out.println(); for (Integer i: new IteratorGenerator(18)) { System.out.print(i + " "); } } } public class Generators { public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen, int n) { for (int i = 0; i < n; i++) { coll.add(gen.next()); } return coll; } public static void main(String[] args) { Collection<Integer> f = fill(new ArrayList<Integer>(), new IteratorGenerator(), 12); for (int i: f) { System.out.print(i + " "); } } }
一個通用的Generator
咱們能夠爲任何一個類構造一個Generator:
import java.util.*; interface Generator<T> { T next(); } class IteratorGenerator<T> implements Generator<T> { private Class<T> type; public IteratorGenerator(Class<T> type) { this.type = type; } public T next() { try { return type.newInstance(); } catch(Exception e) { throw new RuntimeException(e); } } public static <T> Generator<T> create(Class<T> type) { return new IteratorGenerator<T>(type); } } class CountedObject { private static long counter = 0; private final long id = counter++; public long id() { return id; } public String toString() { return "CountedObject " + id; } } public class Generators { public static void main(String[] args) { Generator<CountedObject> gen = IteratorGenerator.create(CountedObject.class); for (int i = 0; i < 5; i++) { System.out.println(gen.next()); } } }
一個Set實用工具
import java.util.*; public class Sets { public static <T> Set<T> union(Set<T> a, Set<T> b) { Set<T> result = new HashSet<T>(a); result.addAll(b); return result; } public static <T> Set<T> intersection(Set<T> a, Set<T> b) { Set<T> result = new HashSet<T>(a); result.retainAll(b); return result; } public static <T> Set<T> difference(Set<T> superset, Set<T> subset) { Set<T> result = new HashSet<T>(superset); result.removeAll(subset); return result; } public static <T> Set<T> complement(Set<T> a, Set<T> b) { return difference(union(a, b), intersection(a, b)); } public static void main(String[] args) { Set<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4)); Set<Integer> s2 = new HashSet<>(Arrays.asList(3, 4, 5, 6)); System.out.println(Sets.union(s1, s2)); System.out.println(Sets.intersection(s1, s2)); System.out.println(Sets.difference(s1, s2)); System.out.println(Sets.complement(s1, s2)); } }
咱們可使用匿名內部類生成next方法, 而後在fill函數中調用便可.
import java.util.*; interface Generator<T> { T next(); } class Customer { private static long counter = 1; private final long id = counter++; private Customer() {} public String toString() { return "Customer " + id; } public static Generator<Customer> generator() { return new Generator<Customer>() { @Override public Customer next() { return new Customer(); } }; } } class Generators { public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen, int n) { for (int i = 0; i < n; i++) { coll.add(gen.next()); } return coll; } } public class BankTeller { public static void main(String[] args) { Queue<Customer> line = new LinkedList<>(); Generators.fill(line, Customer.generator(), 15); for (Customer c: line) { System.out.println(c); } } }
何爲擦除
在使用泛型編碼時, 任何關於泛型的類型信息都會被擦除成最原始的類型: Object.
驗證1: 不一樣類型的List, 其class永遠爲List.class
import java.util.*; public class Test { public static void main(String[] args) { List<String> l1 = new ArrayList<>(); List<Integer> l2 = new ArrayList<>(); // true System.out.print(l1.getClass() == l2.getClass()); try { // true System.out.print(l2.getClass() == Class.forName("java.util.ArrayList")); } catch (Exception e) { throw new RuntimeException(e); } } }
驗證2: 其泛型參數, 如String, Integer, 會最終用標識符如T, E等表示:
import java.util.*; public class Test { public static void main(String[] args) { List<String> l1 = new ArrayList<>(); // [E] System.out.print(Arrays.toString(l1.getClass().getTypeParameters())); } }
C++實現模板的方式和Java實現模板方式的比較
C++的代碼:
#include <iostream> using namespace std; template<class T> class Manipulator { T obj; public: Manipulator(T x) { obj = x; } void manipulate() { obj.f(); } }; class HasF { public: void f() { cout << "HasF::f()" << endl; } }; int main() { HasF hf; Manipulator<HasF> manipulator(hf); manipulator.manipulate(); }
Java代碼:
class Manipulator<T> { T obj; Manipulator(T x) { obj = x; } public void manipulator() { obj.f(); } } class HasF { public void f() { System.out.println("HasF::f()"); } } public class Test { public static void main(String[] args) { HasF hf = new HasF(); Manipulator<HasF> m = new Manipulator<>(hf); m.manipulator(); } }
這裏因爲obj已經被擦除成Object類型, 因此會致使編譯錯誤.
咱們只要讓obj最多被擦除成HasF類型便可, 可以使用語法: <T extends HasF>, 這說明T能夠爲HasF或其子類, 則T最多被擦除爲基類HasF類型, 從而致使obj.f()有意義.
class Manipulator<T extends HasF>
擦除的來源
泛型並非Java1.0的語言特性, 因此它沒法達到上例C++中的"具體化"效果.
假設泛型做爲其語言特性, 實現了具體化, 則咱們能夠在類型參數上執行基於類型的語言操做和反射操做(如轉型, new表達式, instanceof操做).
public class Erased<T> { private final int SIZE = 100; public static void f(Object arg) { // error if (arg instanceof T) {} // error T var = new T(); // error T[] arr = new T[SIZE]; } }
擦除的核心的動機是: 使得泛化的客戶端能夠用非泛化的類庫來使用. 由於Java SE5以前是沒有泛化的, 而若是但願SE5以後的返回代碼兼容以前的代碼, 則須要將泛化的代碼擦除爲Object類型.
擦除例子1:
import java.lang.reflect.Array; import java.util.Arrays; public class ArrayMarker<T> { private Class<T> kind; public ArrayMarker(Class<T> kind) { this.kind = kind; } @SuppressWarnings("unchecked") T[] create(int size) { return (T[]) Array.newInstance(kind, size); } public static void main(String[] args) { ArrayMarker<String> stringMarker = new ArrayMarker<>(String.class); String[] stringArray = stringMarker.create(9); // [null, null, null, null, null, null, null, null, null] System.out.println(Arrays.toString(stringArray)); } }
即便kind被存儲爲Class<T>, 擦除也意味着它實際上被存儲爲Class, 沒有任何參數.
這裏主要問題在於: 咱們須要操做T類型來建立數組, 而擦除正好影響到了T的具體類型.
擦除例子2:
import java.util.*; public class FilledListMarker<T> { List<T> create(T t, int n) { List<T> result = new ArrayList<>(); for (int i = 0; i < n; i++) { result.add(t); } return result; } public static void main(String[] args) { FilledListMarker<String> stringMarker = new FilledListMarker<>(); List<String> list = stringMarker.create("hello", 4); // [hello, hello, hello, hello] System.out.println(list); } }
這裏擦除T根本不會影響到代碼的任何一個細節. 內部的操做是基於List的.
擦除補償
因爲擦除致使了類型爲Object, 則它不能執行相似new, instanceof等操做. 這時候須要補償其擦除形成的影響.
1. 使用isInstance來代替isinstanceof:
class Building {} class House extends Building {} public class ClassTypeCapture<T> { Class<T> kind; public ClassTypeCapture(Class<T> kind) { this.kind = kind; } public boolean f(Object arg) { return kind.isInstance(arg); } public static void main(String[] args) { ClassTypeCapture<Building> ctt1 = new ClassTypeCapture(Building.class); System.out.println(ctt1.f(new Building())); System.out.println(ctt1.f(new House())); ClassTypeCapture<Building> ctt2 = new ClassTypeCapture(House.class); System.out.println(ctt2.f(new Building())); System.out.println(ctt2.f(new House())); } }
2. 使用工廠模式, 經過傳遞Class對象調用newInstance來解決new T()無效的狀況:
class ClassAsFactory<T> { T x; public ClassAsFactory(Class<T> kind) { try { x = kind.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } } class Employee {} public class InstantianteGenericType { public static void main(String[] args) { ClassAsFactory<Employee> fe = new ClassAsFactory<>(Employee.class); System.out.println("ClassAsFactory<Employee> succeeded"); try { ClassAsFactory<Integer> fi = new ClassAsFactory<>(Integer.class); } catch (Exception e) { System.out.println("ClassAsFactory<Integer> failed"); } } }
newInstance()會調用類的默認構造函數, 而Integer類沒有默認構造函數.
3. 使用ArrayList來代替泛型數組:
public class ListOfGenerics<T> { private List<T> arr = new ArrayList<>(); public void add(T item) { array.add(item); } public T get(int index) { return array.get(index); } }
數組之因此不能夠泛型在於: 數組的自己特性要求它們必須知道元素的類型和元素的個數(這樣數組能夠進行分配肯定的內存空間大小). 可是擦除的存在致使運行時候並不知道其元素的類型, 而數組並無動態建立的能力(由於ArrayList有, 因此才用ArrayList來解決泛型數組所遇到的難題).