标签:
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