Generics泛型的应用
JDK 5 中的泛型
Generics(泛型)
泛型的第一个优点是实现编译时期检查;第二个优点是在使用collection框架的时候不需要cast了。
一、先来了解一下JDK5以前是怎么处理的。
在JDK5之前的Collection框架以Object为参数,这样整个Collection框架可以变得通用,而不是对于每个数据类型都定义一个Collection。
比如,List的add方法: public boolean add(java.lang.Object element)
但是当你使用的时候,get()方法是这样声明的:
public java.lang.Object get(int index)
throws IndexOutOfBoundsException
请看下面的例子:
List stringList1 = new ArrayList(); stringList1.add("Java 5"); stringList1.add("with generics"); String s1 = (String) stringList1.get(0); |
一切看起来ok,但是如果这样
List stringList1 = new ArrayList(); stringList1.add("Java 5"); stringList1.add(new Date()); stringList1.add("with generics"); String s1 = (String) stringList1.get(1); |
看起来也ok,运行起来就出现
java.lang.ClassCastException: java.util.Date
二、应用泛型
正如方法可以接收参数,泛型类型也可以接收参数,这也是为什么generic type也称作 “parameterized type”。用<>(angle bracket)将参数传递给泛型。
例如:java.util.List,可以写作 List<E> myList,其中E称作类型变量。在JDK5中可以如下实例化:
List<String> myList = new ArrayList<String>();
在JDK5中,代码可以如下:
List<String> stringList2 = new ArrayList<String>(); stringList2.add("Java 5.0"); stringList2.add("with generics"); // no need for type casting String s2 = stringList2.get(0); System.out.println(s2.toUpperCase()); |
如果你尝试将Date()加入这个List
List<String> stringList2 = new ArrayList<String>(); stringList2.add("Java 5.0"); stringList2.add("with generics"); stringList2.add(new Date()); // no need for type casting String s2 = stringList2.get(0); System.out.println(s2.toUpperCase()); |
系统会提示:
The method add(String) in the type List<String> is not applicable for the arguments (Date)
比较前面JDK5之前的处理,已经实现了编译期检查,不会将错误留到运行时候。
三、比较JDK5和之前的处理
import java.util.List; import java.util.ArrayList;
public class GenericListTest { public static void main(String[] args) { // in JDK 1.4 List stringList1 = new ArrayList(); stringList1.add("Java 1.0 - 5.0"); stringList1.add("without generics"); // cast to java.lang.String String s1 = (String) stringList1.get(0); System.out.println(s1.toUpperCase());
// now with generics in JDK 5 List<String> stringList2 = new ArrayList<String>(); stringList2.add("Java 5.0"); stringList2.add("with generics"); // no need for type casting String s2 = stringList2.get(0); System.out.println(s2.toUpperCase()); } }
|
下面是一个有趣的应用:
import java.util.ArrayList; import java.util.List; public class ListOfListsTest { public static void main(String[] args) { List<String> listOfStrings = new ArrayList<String>(); listOfStrings.add("Hello again"); List<List<String>> listOfLists = new ArrayList<List<String>>(); listOfLists.add(listOfStrings); String s = listOfLists.get(0).get(0); System.out.println(s); // prints "Hello again" } } |
四、泛型进阶
对于没有使用泛型处理的代码,在JDK5中,系统会出现编译警告,提示代码为”raw type”。
这是兼容的,如果不想看到这个信息,可以做如下处理:
1, 使用 –source 1.4 参数编译。
2, 使用@SupressWarnings(“unchecked”) annotaion
3, 升级为使用泛型
如果你声明一个泛型List<atype>,那么List的实现中,atype可以是如下类型:
1, atype的实例
2, atype子类的实例(如果atype是一个class)
3, atype的实现类的实例(如果atype是一个接口)
泛型和Java对象一样,都有自己的类型,下面两个就是完全独立,没有关系的:
List<Object> list1 = new ArrayList<Object>();
List<String> list2 = new ArrayList<String>();
虽然String是Object的子类,但是List<String>可不是List<Object>的子类。
再看下面的代码,
import java.util.ArrayList; import java.util.List;
public class AllowedTypeTest { public static void doIt(List<Object> l) { } public static void main(String[] args) { List<String> myList = new ArrayList<String>(); // this will generate a compile error doIt(myList); } } |
系统会提示编译错误。
为了解决类似的问题,JDK5提出了通配符号(?),List<?>,这个声明意味着List可以接收任何对象。
import java.util.ArrayList; import java.util.List;
public class WildCardTest {
public static void printList(List<?> list) { for (Object element : list) { System.out.println(element); } } public static void main(String[] args) { List<String> list1 = new ArrayList<String>(); list1.add("Hello"); list1.add("World"); printList(list1);
List<Integer> list2 = new ArrayList<Integer>(); list2.add(100); list2.add(200); printList(list2); } } |
特别注意,如果你这样声明就不对了:
List<?> myList = new ArrayList<?>();
边界通配符 (bounded wildcards)
在泛型中,如果你有一个getAverage方法用来返回平均值,你可能要传递integer,float,double等。那么你将参数设置为List<Number>,但是由于List<Number>、List<Integer>、List<Double>是不同的类型,所以不行。我们还可以用List<?>,但是这样会降低类型安全,因为List<String>也可能发生。
JDK5推出upper bounded ,格式如下:
GenericType <? Extends uppderBoundedType>
请看下面的例子:
import java.util.ArrayList; import java.util.List; public class BoundedWildcardTest { public static double getAverage(List<? extends Number> numberList) { double total = 0.0; for (Number number : numberList) total += number.doubleValue(); return total/numberList.size(); }
public static void main(String[] args) { List<Integer> integerList = new ArrayList<Integer>(); integerList.add(3); integerList.add(30); integerList.add(300); System.out.println(getAverage(integerList)); // 111.0 List<Double> doubleList = new ArrayList<Double>(); doubleList.add(3.0); doubleList.add(33.0); System.out.println(getAverage(doubleList)); // 18.0 } } |
It’s so ok
如果在main()