術語 | 例子 |
---|---|
參數化類型(Parameterized type) | List<String> |
實際類型參數(Actual type parameter) | String |
泛型類型(Generic type) | List<E> |
形式類型參數(Formal type parameter) | E |
無限制通配符類型(Unbounded wildcard type) | List<?> |
原始類型(Raw type) | List |
有限制類型參數(Bounded type parameter) | <E extends Number> |
遞歸類型限制(Recursive type bound) | <T extends Comparable<T>> |
有限制通配符類型(Bounded wildcard type) | List<? extends Number> |
泛型方法(Generic method) | static <E> List< E > asList(E[] a) |
類型令牌(Type token) | String.class |
// 按照這麼寫並不會報錯(List內部由一個Object數組維護),可是使用上很容易出錯。 List list = new ArrayList(); list.add("Hello"); list.add(100);
public <T> T[] toArray(T[] a) { if (a.length < size) { @SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass()); return result; } System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
// 運行時報錯 Object[] objectArray = new Long[1]; objectArray[0] = "I don't fit in"; // Throws ArrayStoreException // 沒法編譯經過 List<Object> ol = new ArrayList<Long>(); // Incompatible types ol.add("I don't fit in");
數組在運行時才知道並檢查元素類型。html
E,List<E>,和List<String>這些類型在技術上都被稱爲不可具化類型,即運行時展現信息比編譯時展現信息要少的類型。java
public class Stack<E> { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; // The elements array will contain only E instances from push(E). // This is sufficient to ensure type safety, but the runtime // type of the array won't be E[]; it will always be Object[]! @SuppressWarnings("unchecked") public Stack() { elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size++] = e; } public E pop() { if (size == 0) throw new EmptyStackException(); E result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } // no changes in isEmpty or ensureCapacity }
public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; }
public static <E> Set<E> union(Set<E> s1, Set<E> s2) { Set<E> result = new HashSet<>(s1); result.addAll(s2); return result; }
public static <T> UnaryOperator<T> identityFunction() { return (t) -> t; }
private static UnaryOperator<Object> IDENTITY_FN = (t) -> t; @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identityFunction() { return (UnaryOperator<T>) IDENTITY_FN; }
public static <E extends Comparable<E>> E max(Collection<E> c) { if (c.isEmpty()) throw new IllegalArgumentException("Empty collection"); E result = null; for (E e : c){ if (result == null || e.compareTo(result) > 0){ result = Objects.requireNonNull(e); } } return result; }
注:當列表是空的時候,這個方法會拋出IllegalArgumentException異常。一種更好的辦法是返回一個Optional<E>。api
public class Stack<E> { public Stack(); public void push(E e); public E pop(); public boolean isEmpty(); // 生產者 public void pushAll(Iterable<? extends E> src) { for (E e : src) push(e); } // 消費者 public void popAll(Collection<? super E> dst) { while (!isEmpty()) dst.add(pop()); } } // 使用pushAll Stack<Number> numberStack = new Stack<>(); Iterable<Integer> integers = ... ; numberStack.pushAll(integers); // 使用popAll Stack<Number> numberStack = new Stack<Number>(); Collection<Object> objects = ... ; numberStack.popAll(objects);
// 修改前 public static <E> Set<E> union(Set<E> s1, Set<E> s2); // 修改後 public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2); // 使用 Set<Integer> integers = Set.of(1, 3, 5); Set<Double> doubles = Set.of(2.0, 4.0, 6.0); Set<Number> numbers = union(integers, doubles);
具體參考:泛型總結數組
// 修改前 public static <T extends Comparable<T>> T max(List<T> list); // 修改後 public static <T extends Comparable<? super T>> T max(List<? extends T> list); // 使用 // ScheduledFuture不直接實現Comparable,可是它的父類接口實現了Comparable,因此爲了支持這種狀況須要修改成<T extends Comparable<? super T>>。 List<ScheduledFuture<?>> scheduledFutures = ... ; max(scheduledFutures);
// 無界類型參數 public static <E> void swap(List<E> list, int i, int j); // 無界通配符:該方式優於上一個方式,可是因爲無界通配符類型沒法修改,即須要藉助helper進行修改,但這對於調用者無需關心。 public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j); } private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); }
public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<>(); public <T> void putFavorite(Class<T> type, T instance) { favorites.put(Objects.requireNonNull(type), instance); } // 確保類型安全,不安全將拋出異常 public <T> void putFavorite(Class<T> type, T instance) { favorites.put(type, type.cast(instance)); } public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } } // 使用 public static void main(String[] args) { Favorites f = new Favorites(); f.putFavorite(String.class, "Java"); f.putFavorite(Integer.class, 0xcafebabe); f.putFavorite(Class.class, Favorites.class); String favoriteString = f.getFavorite(String.class); int favoriteInteger = f.getFavorite(Integer.class); Class<?> favoriteClass = f.getFavorite(Class.class); System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName()); }
// 表示類,方法,屬性和其餘程序元素的反射類型實現 public interface AnnotatedElement { <T extends Annotation> T getAnnotation(Class<T> annotationType); } // 若是一個Class<?>的對象但願傳遞給接收Class<T>的泛型方法,能夠將對象轉換爲Class<? extends Annotation>,可是會有編譯時警告,因此須要藉助asSubclass轉換所調用的Class對象來表示由其參數表示的類的子類。 static Annotation getAnnotation(AnnotatedElement element,String annotationTypeName) { Class<?> annotationType = null; // Unbounded type token try { annotationType = Class.forName(annotationTypeName); } catch (Exception ex) { throw new IllegalArgumentException(ex); } return element.getAnnotation(annotationType.asSubclass(Annotation.class)); }