标签:
1. 泛型的引入(JDK 1.5)例如,类ArrayList就是被设计者设计成泛型,它带有一个泛型类型<E>。
public class ArrayList<E> implements List<E> .... { // Constructor public ArraList() { ...... } // Public methods public boolean add(E e) { ...... } public void add(int index, E element) { ...... } public boolean addAll(int index, Collection<? extends E> c) public abstract E get(int index) { ...... } public E remove(int index) ....... }
ArrayList<Integer> lst1 = new ArrayList<Integer>(); // E substituted with Integer lst1.add(0, new Integer(88)); lst1.get(0); ArrayList<String> lst2 = new ArrayList<String>(); // E substituted with String lst2.add(0, "Hello"); lst2.get(0);
// Pre-JDK 1.5 import java.util.*; public class ArrayListWithoutGenericsTest { public static void main(String[] args) { List strLst = new ArrayList(); // List and ArrayList holds Objects strLst.add("alpha"); // String upcast to Object implicitly strLst.add("beta"); strLst.add("charlie"); Iterator iter = strLst.iterator(); while (iter.hasNext()) { String str = (String)iter.next(); // need to explicitly downcast Object back to String System.out.println(str); } strLst.add(new Integer(1234)); // Compiler/runtime cannot detect this error String str = (String)strLst.get(3); // compile ok, but runtime ClassCastException } }为了解决这个问题,也就是在编译的时候就可以进行类型的检查,这样就引入了泛型。
2. 泛型
下面是一个自定义的ArrayList版本,叫做MyArrayList,它没有使用泛型。
// A dynamically allocated array which holds a collection of java.lang.Object - without generics public class MyArrayList { private int size; // number of elements private Object[] elements; public MyArrayList() { // constructor elements = new Object[10]; // allocate initial capacity of 10 size = 0; } public void add(Object o) { if (size < elements.length) { elements[size] = o; } else { // allocate a larger array and add the element, omitted } ++size; } public Object get(int index) { if (index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return elements[index]; } public int size() { return size; } }从上面很轻易的就可以看出MyArrayList不是类型安全的,例如,如果我们希望创建一个存放String类型对象的MyArrayList,但是我们向里面添加了一个Integer对象,编译器是不能检查出异常,这是因为我们MyArrayList设计的是存放Object类型的元素,并且任何对象类型都可以向上转换为Object类型的对象。
public class MyArrayListTest { public static void main(String[] args) { // Intends to hold a list of Strings, but not type-safe MyArrayList strLst = new MyArrayList(); // adding String elements - implicitly upcast to Object strLst.add("alpha"); strLst.add("beta"); // retrieving - need to explicitly downcast back to String for (int i = 0; i < strLst.size(); ++i) { String str = (String)strLst.get(i); System.out.println(str); } // Inadvertently added a non-String object will cause a runtime // ClassCastException. Compiler unable to catch the error. strLst.add(new Integer(1234)); // compiler/runtime cannot detect this error for (int i = 0; i < strLst.size(); ++i) { String str = (String)strLst.get(i); // compile ok, runtime ClassCastException System.out.println(str); } } }
从上面可以看出,如果我们想创建一个String类型的List,但是我们添加了一个非String类型的对象元素,这个对象同样是可以向上转换为Object对象类型的,并且编译器自动完成,编译器并不能检查它是否合法,这样就存在一个隐患,当我们获取这个元素的时候,它是一个Object类型,我们需要手动转换为String类型,这个时候就会抛出ClassCastException异常,它发生在运行时期。
2.1 泛型类
JDK 1.5引入了所谓的泛型来解决这一问题,泛型允许我们去进行类型的抽象,我们可以创建一个泛型类并且在类实例化的时候指定具体类型信息。编译器在编译器期间会进行相应的类型检查,这样就确保了在运行时期不会有类型转换的异常发生,这就是所谓的类型安全。
下面我们来看看java.util.List<E>的声明接口。
public interface List<E> extends Collection<E> { boolean add(E o); void add(int index, E element); boolean addAll(Collection<? extends E> c); boolean containsAll(Collection<?> c); ...... }
例如:方法的定义,声明形参
// A method's definition public static int max(int a, int b) { // int a, int b are formal parameters return (a > b) ? a : b; }方法的调用,传递实参
// Invocation: formal parameters substituted by actual parameters int maximum = max(55, 66); // 55 and 66 are actual parameters int a = 77, b = 88; maximum = max(a, b); // a and b are actual parameters返回到上面的java.util.List<E>,假如我们创建的了一个List<Integer>,这个时候,形参类型E就接受到了一个实参类型<Integer>,这样我们在使用E的时候实际就是使用这个实参类型Integer了。
正式的类型参数命名规范
一般使用一个大写的字母作为类型参数。例如:
public class GenericBox<E> { // Private variable private E content; // Constructor public GenericBox(E content) { this.content = content; } public E getContent() { return content; } public void setContent(E content) { this.content = content; } public String toString() { return content + " (" + content.getClass() + ")"; } }
public class TestGenericBox { public static void main(String[] args) { GenericBox<String> box1 = new GenericBox<String>("Hello"); String str = box1.getContent(); // no explicit downcasting needed System.out.println(box1); GenericBox<Integer> box2 = new GenericBox<Integer>(123); // autobox int to Integer int i = box2.getContent(); // downcast to Integer, auto-unbox to int System.out.println(box2); GenericBox<Double> box3 = new GenericBox<Double>(55.66); // autobox double to Double double d = box3.getContent(); // downcast to Double, auto-unbox to double System.out.println(box3); } }
Hello (class java.lang.String) 123 (class java.lang.Integer) 55.66 (class java.lang.Double)
下面我们返回到上面我们写的MyArrayList的例子,我们知道它不是一个泛型类型,下面我们来写一个泛型的版本。
// A dynamically allocated array with generics public class MyGenericArrayList<E> { private int size; // number of elements private Object[] elements; public MyGenericArrayList() { // constructor elements = new Object[10]; // allocate initial capacity of 10 size = 0; } public void add(E e) { if (size < elements.length) { elements[size] = e; } else { // allocate a larger array and add the element, omitted } ++size; } public E get(int index) { if (index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return (E)elements[index]; } public int size() { return size; } }
MyGenericArrayList<E>被编译器处理后形式如下:
// The translated code public class MyGenericArrayList { private int size; // number of elements private Object[] elements; public MyGenericArrayList() { // constructor elements = new Object[10]; // allocate initial capacity of 10 size = 0; } // Compiler replaces E with Object, but check e is of type E, when invoked to ensure type-safety public void add(Object e) { if (size < elements.length) { elements[size] = e; } else { // allocate a larger array and add the element, omitted } ++size; } // Compiler replaces E with Object, and insert downcast operator (E<E>) for the return type when invoked public Object get(int index) { if (index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return (Object)elements[index]; } public int size() { return size; } }
public class MyGenericArrayListTest { public static void main(String[] args) { // type safe to hold a list of Strings MyGenericArrayList<String> strLst = new MyGenericArrayList<String>(); strLst.add("alpha"); // compiler checks if argument is of type String strLst.add("beta"); for (int i = 0; i < strLst.size(); ++i) { String str = strLst.get(i); // compiler inserts the downcasting operator (String) System.out.println(str); } strLst.add(new Integer(1234)); // compiler detected argument is NOT String, issues compilation error } }
2.2 泛型方法
方法也可以定义为泛型类型,例如:
public static <E> void ArrayToArrayList(E[] a, ArrayList<E> lst) { for (E e : a) lst.add(e); }在泛型方法中,需要在返回类型之前声明类型参数,这样类型参数就可以在方法的参数列表或者返回类型上使用了。
和泛型类相似,当编译器也会使用Object类型来替换参数类型E,例如上面的代码被编译器处理后形式如下:
public static void ArrayToArrayList(Object[] a, ArrayList lst) { // compiler checks if a is of type E[], // lst is of type ArrayList<E> for (Object e : a) lst.add(e); // compiler checks if e is of type E }
import java.util.*; public class TestGenericMethod { public static <E> void ArrayToArrayList(E[] a, ArrayList<E> lst) { for (E e : a) lst.add(e); } public static void main(String[] args) { ArrayList<Integer> lst = new ArrayList<Integer>(); Integer[] intArray = {55, 66}; // autobox ArrayToArrayList(intArray, lst); for (Integer i : lst) System.out.println(i); String[] strArray = {"one", "two", "three"}; //ArrayToArrayList(strArray, lst); // Compilation Error below } }
另外,在泛型方法中,泛型有一个可选的语法就是指定泛型方法中的类型。你可以将指定的真实类型放在点操作符和方法名之间。
TestGenericMethod.<Integer>ArrayToArrayList(intArray, lst);这个语法可以增加代码的可读性,另外可以在类型模糊的地方来指定泛型类型。
2.3 通配符
对于下面这行代码
ArrayList<Object> lst = new ArrayList<String>();它会出现类型不兼容的错误,因为ArrayList<String>不是一个ArrayList<Object>。这个问题貌似混淆了多态的概念,因为在多态中子类实例是可以分配给父类引用的。
对应下面代码:
List<String> strLst = new ArrayList<String>(); // 1 List<Object> objLst = strList; // 2 - Compilation Error非受限的通配符<?>跟上面一样,第二行代码会出现编译错误,但是如果第二行代码成功的话,就会出现另一个问题:任意的对象都可以添加到strList中,这又会引起类型不安全的问题。
import java.util.*; public class TestGenericWildcard { public static void printList(List<Object> lst) { // accept List of Objects only, // not List of subclasses of object for (Object o : lst) System.out.println(o); } public static void main(String[] args) { List<Object> objLst = new ArrayList<Object>(); objLst.add(new Integer(55)); printList(objLst); // matches List<String> strLst = new ArrayList<String>(); strLst.add("one"); printList(strLst); // compilation error } }
非受限的通配符<?>
为了解决这个问题,泛型中引入了一个通配符(?),它代表任何未知类型,例如我们可以重写上面的printList()方法,它可以接受任何未知类型的List。
public static void printList(List<?> lst) { for (Object o : lst) System.out.println(o); }
通配符<? extends type>表示接受类型type以及它的子类,例如:
public static void printList(List<? extends Number> lst) { for (Object o : lst) System.out.println(o); }
很显然,<?>可以理解为<? extends Object>,因为它可以接受任何对象类型。
下限通配符<? super type>
跟上限通配符类似,<? super type>表示接受的类型是type以及type的父类
2.4 受限泛型
在使用泛型的时候,我们也可以使用上面的限制来指定参数类型。例如:<T extends Number>表示接收Number以及它的子类(例如:Integer 和 Double)
例子
下面方法add()中声明了参数类型<T extends Number>
public class MyMath { public static <T extends Number> double add(T first, T second) { return first.doubleValue() + second.doubleValue(); } public static void main(String[] args) { System.out.println(add(55, 66)); // int -> Integer System.out.println(add(5.5f, 6.6f)); // float -> Float System.out.println(add(5.5, 6.6)); // double -> Double } }
例如:
public class TestGenericsMethod { public static <T extends Comparable<T>> T maximum(T x, T y) { return (x.compareTo(y) > 0) ? x : y; } public static void main(String[] args) { System.out.println(maximum(55, 66)); System.out.println(maximum(6.6, 5.5)); System.out.println(maximum("Monday", "Tuesday")); } }
public static Comparable maximum(Comparable x, Comparable y) { // replace T by upper bound type Comparable // Compiler checks x, y are of the type Comparable // Compiler inserts a type-cast for the return value return (x.compareTo(y) > 0) ? x : y; }
(Comparable)maximum(55, 66); (Comparable)maximum(6.6, 5.5); (Comparable)maximum("Monday", "Tuesday");
原文链接:Java Programming Tutorial Generics
标签:
原文地址:http://blog.csdn.net/hp910315/article/details/51028483