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来解决泛型数组所遇到的难题).