国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Effective java筆記-泛型

2019-11-06 07:28:24
字體:
來源:轉載
供稿:網友

泛型

第23條 請不要在新代碼中使用原生態類型

書上示例代碼:public class Raw { // Uses raw type (List) - fails at runtime! - Page 112 public static void main(String[] args) { List<String> strings = new ArrayList<String>(); unsafeAdd(strings, new Integer(42)); String s = strings.get(0); // Compiler-generated cast } PRivate static void unsafeAdd(List list, Object o) { list.add(o); } // Use of raw type for unknown element type - don't do this! - Page 113 static int rawNumElementsInCommon(Set s1, Set s2) { int result = 0; for (Object o1 : s1) if (s2.contains(o1)) result++; return result; } //如果要使用泛型,但不確定或者不關心實際的類型參數(如上面的例子),就可以使用一個問好代替。例如,泛型Set<E>的無限制通配符類型為Set<?>(讀作“某個類型的集合”) // Unbounded wildcard type - typesafe and flexible - Page 113 static int numElementsInCommon(Set<?> s1, Set<?> s2) { int result = 0; for (Object o1 : s1) if (s2.contains(o1)) result++; return result; }} //為什么說Set<?>是類型安全的:你不能將除了null外的任何元素放入Collection<?>中

第24條 消除非受檢警告

如果無法消除警告,同時可以證明引起警告的代碼是類型安全的,(只有在這種情況下才)可以用一個@SuppressWarnings(“unchecked”)注解來禁止這條警告,并且要在盡可能小粒度上使用該注解。

第25條 列表優先于數組

1.數組是協變的(covariant),列表是不可變的(invariant):如果Sub為Super的子類型,那么Sub[]就是Super[]的子類型;對于任意兩個不同的類型Type1和Type2,List<Type1>既不是List<Type2>的子類型,也不是List<Type2>超類型。2.數組是具體化的(reified)。因此數組會在運行時才知道并檢查它們的元素類型約束。泛型時通過擦除(erasure)來實現的,因此泛型只在編譯時強化它們的類型信息,并在運行時丟棄(或擦除)它們的元素類型信息。3.由于以上兩點,數組和泛型不能很好的混合使用(創建泛型、參數化類型或類型參數的數組時非法的),為什么非法:因為它們不是類型安全的,要是它合法,編譯器在其他正確程序中的轉換就會失敗,**這就違背了泛型系統提供的基本保證**,例如:List<String>[] stringLists = new List<String>[1];List<Integer> intList = Arrays.asList(42);Object[] objects = stringLists;objects[0] = intList;String s = stringLists[0].get(0);//ClassCastException直觀的說,不可具體化的類型是指其運行時表示法包含的信息比他編譯時表示法包含的信息更少的類型。唯一可具體化的參數化類型是無限制的通配符類型,雖然不常用,但是創建無限制通配符類型的數組是合法的。25條后半段涉及到什么同步列表,沒看懂,貼下代碼:interface Function<T> { T apply(T arg1, T arg2);}public class Reduction { static <E> E reduce(List<E> list, Function<E> f, E initVal) { List<E> snapshot; synchronized (list) { snapshot = new ArrayList<E>(list); } E result = initVal; for (E e : snapshot) result = f.apply(result, e); return result; } // A few sample functions private static final Function<Integer> SUM = new Function<Integer>() { public Integer apply(Integer i1, Integer i2) { return i1 + i2; } }; private static final Function<Integer> PRODUCT = new Function<Integer>() { public Integer apply(Integer i1, Integer i2) { return i1 * i2; } }; private static final Function<Integer> MAX = new Function<Integer>() { public Integer apply(Integer i1, Integer i2) { return Math.max(i1, i2); } }; private static final Function<Integer> MIN = new Function<Integer>() { public Integer apply(Integer i1, Integer i2) { return Math.min(i1, i2); } }; public static void main(String[] args) { List<Integer> intList = Arrays.asList(2, 7, 1, 8, 2, 8, 1, 8, 2, 8); // Reduce intList using each of the above reducers System.out.println(reduce(intList, SUM, 0)); System.out.println(reduce(intList, PRODUCT, 1)); System.out.println(reduce(intList, MAX, Integer.MIN_VALUE)); System.out.println(reduce(intList, MIN, Integer.MAX_VALUE)); }}

第26條 優先考慮泛型

考慮自己編寫一個帶泛型的Stack,第一種:public class EmptyStackException extends RuntimeException {}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; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } // Little program to exercise our generic Stack public static void main(String[] args) { Stack<String> stack = new Stack<String>(); for (String arg : args) stack.push(arg); while (!stack.isEmpty()) System.out.println(stack.pop().toUpperCase()); }}第二種:public class EmptyStackException extends RuntimeException {}public class Stack<E> { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size++] = e; } // Appropriate suppression of unchecked warning public E pop() { if (size == 0) throw new EmptyStackException(); // push requires elements to be of type E, so cast is correct @SuppressWarnings("unchecked") E result = (E) elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } // Little program to exercise our generic Stack public static void main(String[] args) { Stack<String> stack = new Stack<String>(); for (String arg : args) stack.push(arg); while (!stack.isEmpty()) System.out.println(stack.pop().toUpperCase()); }}在上面兩個示例中我們都使用了數組,這似乎于第25條矛盾,但是在有些地方為了性能的考慮必需用數組如ArrayList<E>,Stack<E>中。當數組與泛型一起出現的時候就可能出現一些問題,比如在第一種實現中我們我們聲明了elements為E[],但java中無法new E[],所以采用了強制類型轉換(E[])。第二種實現中,elements被聲明為Object[],所以某些地方就要強制轉換(E)。推薦第二種。

第27條 優先考慮泛型方法

public class Union { // Generic method public static <E> Set<E> union(Set<E> s1, Set<E> s2) { Set<E> result = new HashSet<E>(s1); result.addAll(s2); return result; } // Simple program to exercise generic method public static void main(String[] args) { Set<String> guys = new HashSet<String>(Arrays.asList("Tom", "Dick", "Harry")); Set<String> stooges = new HashSet<String>(Arrays.asList("Larry", "Moe", "Curly")); Set<String> aflCio = union(guys, stooges); System.out.println(aflCio); }}上面union方法的局限性在于,三個集合的類型(兩個輸入參數和一個返回值)必需全部相同。利用有限制的通配符類型,可以使這個方法變得更加靈活(見第28條)。泛型方法有類型推導的特性,但構造器沒有,可以利用這個特性,使創建參數化類型實例變得更加輕松(第一條就已經談過):public class GenericStaticFactory { // Generic static factory method public static <K, V> HashMap<K, V> newHashMap() { return new HashMap<K, V>(); } public static void main(String[] args) { // Parameterized type instance creation with static factory Map<String, List<String>> anagrams = newHashMap(); }}泛型單例工廠:有時會需要創建不可變但又適合于許多不同類型的對象(比如一些函數對象如Collections.reverSEOrder)例子:假設有一個接口,描述了一個方法,該方法接受和返回某個類型T的值:public interface UnaryFunction<T> { T apply(T arg);}現在假設要提供一個恒等函數(identity function)(一個函數對象).可以這樣:public class GenericSingletonFactory { // Generic singleton factory pattern private static UnaryFunction<Object> IDENTITY_FUNCTION = new UnaryFunction<Object>() { public Object apply(Object arg) { return arg; } }; // IDENTITY_FUNCTION is stateless and its type parameter is // unbounded so it's safe to share one instance across all types. @SuppressWarnings("unchecked") public static <T> UnaryFunction<T> identityFunction() { return (UnaryFunction<T>) IDENTITY_FUNCTION; } // Sample program to exercise generic singleton public static void main(String[] args) { String[] strings = { "jute", "hemp", "nylon" }; UnaryFunction<String> sameString = identityFunction(); for (String s : strings) System.out.println(sameString.apply(s)); Number[] numbers = { 1, 2.0, 3L }; UnaryFunction<Number> sameNumber = identityFunction(); for (Number n : numbers) System.out.println(sameNumber.apply(n)); }}遞歸類型限制:public class RecursiveTypeBound { // Returns the maximum value in a list - uses recursive type bound public static <T extends Comparable<T>> T max(List<T> list) { Iterator<T> i = list.iterator(); T result = i.next(); while (i.hasNext()) { T t = i.next(); if (t.compareTo(result) > 0) result = t; } return result; } public static void main(String[] args) { List<String> argList = Arrays.asList(args); System.out.println(max(argList)); }}

第28條 利用有限制通配符來提升API靈活性

如25條所述,參數化類型是不可變的(invariant),這樣如果我們有:public class Stack<E>{ public Stack(); public void push(E e); public E pop(); public boolean isEmpty();} 然后我們想要增加一個方法,讓它按順序將一系列的元素全部放到堆棧中,如下://pushAll method withod wildcard type - dificient!public void pushAll(Iterable<E> src){ for(E e :src) push(e);}假如有一個Stack<Number>,并且調用了pushAll(intVal),這里的intVal就是Integer類型的,這會產生編譯時錯誤,因為參數化類型是不可變的,類似的還有popAll方法,它將Stack中的元素放入另一個容器中。解決方法:public class EmptyStackException extends RuntimeException {}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; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } // pushAll method without wildcard type - deficient! // public void pushAll(Iterable<E> src) { // for (E e : src) // push(e); // } // Wildcard type for parameter that serves as an E producer public void pushAll(Iterable<? extends E> src) { for (E e : src) push(e); } // popAll method without wildcard type - deficient! // public void popAll(Collection<E> dst) { // while (!isEmpty()) // dst.add(pop()); // } // Wildcard type for parameter that serves as an E consumer public void popAll(Collection<? super E> dst) { while (!isEmpty()) dst.add(pop()); } // Little program to exercise our generic Stack public static void main(String[] args) { Stack<Number> numberStack = new Stack<Number>(); Iterable<Integer> integers = Arrays.asList(3, 1, 4, 1, 5, 9); numberStack.pushAll(integers); Collection<Object> objects = new ArrayList<Object>(); numberStack.popAll(objects); System.out.println(objects); }}PECS:producer-extends,consumer-super.上面代碼中pushAll中的src是T的producer,popAll中dst是T的consumer再來看之前已經看過的一些例子,用本條的原則來修改它們以提高其靈活性:interface Function<T> { T apply(T arg1, T arg2);}public class Reduction { // Wildcard type for parameter that serves as an E producer static <E> E reduce(List<? extends E> list, Function<E> f, E initVal) { List<E> snapshot; synchronized (list) { snapshot = new ArrayList<E>(list); } E result = initVal; for (E e : snapshot) result = f.apply(result, e); return result; } private static final Function<Number> MAX = new Function<Number>() { public Number apply(Number n1, Number n2) { return Double.compare(n1.doubleValue(), n2.doubleValue()) > 0 ? n1 : n2; } }; public static void main(String[] args) { // We can use a Number functionto reduce a list of Integer or Double List<Integer> intList = Arrays.asList(2, 7, 1, 8, 2, 8, 1, 8, 2, 8); System.out.println(reduce(intList, MAX, Integer.MIN_VALUE)); List<Double> doubleList = Arrays.asList(2.718281828, 3.141592654, 1.61803399); System.out.println(reduce(doubleList, MAX, Double.NEGATIVE_INFINITY)); }}public class Union { public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) { Set<E> result = new HashSet<E>(s1); result.addAll(s2); return result; } // Simple program to exercise flexible generic method public static void main(String[] args) { Set<Integer> integers = new HashSet<Integer>(); integers.add(1); integers.add(3); integers.add(5); Set<Double> doubles = new HashSet<Double>(); doubles.add(2.0); doubles.add(4.0); doubles.add(6.0); // Won't compile; see page 137 // Set<Number> numbers = union(integers, doubles); // Explicit type parameter is necessary here Set<Number> numbers = Union.<Number> union(integers, doubles);//這里雖然編譯器對泛型方法使用了類型推導,但這里編譯器無法推導出E到底是什么,所以要給他一個顯式的類型參數 System.out.println(numbers); }}public class RecursiveTypeBound { public static <T extends Comparable<? super T>> T max(List<? extends T> list) { Iterator<? extends T> i = list.iterator(); T result = i.next(); while (i.hasNext()) { T t = i.next(); if (t.compareTo(result) > 0) result = t; } return result; } public static void main(String[] args) { List<String> argList = Arrays.asList(args); System.out.println(max(argList)); }}Comparable是T的消費者,因為Comparable消費T并產生一個代表順序關系的整值public class Swap { public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j); } // Private helper method for wildcard capture private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); } public static void main(String[] args) { // Swap the first and last argument and print the resulting list List<String> argList = Arrays.asList(args); swap(argList, 0, argList.size() - 1); System.out.println(argList); }}

第29條 優先考慮類型安全的異構容器

泛型經常用于容器,但如果我們要一個里面可以存不同類型對象的容器,且要保證其類型安全(即類型安全的異構容器),應該將鍵進行參數化(可以用Class對象充當參數化鍵)而不是對容器參數化,這利用了方法的類型推導:public class Favorites { // Typesafe heterogeneous container pattern - implementation private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>(); public <T> void putFavorite(Class<T> type, T instance) { if (type == null) throw new NullPointerException("Type is null"); favorites.put(type, instance); } public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } // Typesafe heterogeneous container pattern - client 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()); }}Favorites實例是類型安全的:當你向它請求String的時候,它從來不會返回一個Integer給你。cast方法是Java的cast操作符的動態模擬。他只檢驗他的參數是否為Class對象所表示的類型的實例,如果是,就返回參數;否則就throw ClassCastException(不過我們知道favorites映射中的值會始終與鍵的類型相匹配)。29條討論Favorites類局限性及其解決方法的部分看不懂
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阜新市| 三江| 宁城县| 宁安市| 临桂县| 胶州市| 霍林郭勒市| 栖霞市| 兖州市| 深泽县| 诏安县| 博罗县| 安塞县| 峨眉山市| 溧水县| 浮山县| 永吉县| 旬阳县| 越西县| 聊城市| 金乡县| 金昌市| 涿州市| 田阳县| 曲阳县| 沧源| 南汇区| 同江市| 白山市| 白沙| 阳泉市| 清丰县| 临清市| 保德县| 恭城| 全椒县| 岑溪市| 西峡县| 明溪县| 醴陵市| 阆中市|