JDK5.0新特性的学习--泛型(上)

没有泛型的日子
所有的java类都源自java.lang.Object,这意味着所有的JAVA对象能转换成Object。因此,在之前的JDK的版本中,很多集合框架的函数接受一个Object参数。所以,collections是一个能持有任何对象的多用途工具,但带来了不良的后果。

举个简单的例子,在JDK 5.0的之前版本中,类List的函数add接受一个Object参数:

成都创新互联成都网站建设按需求定制网站,是成都网站建设公司,为成都电动窗帘提供网站建设服务,有成熟的网站定制合作流程,提供网站定制设计服务:原型图制作、网站创意设计、前端HTML5制作、后台程序开发等。成都网站营销推广热线:18982081108

  1. publicboolean add(java.lang.Object element)


所以你能传递任何类型给add。这是故意这么设计的。否则,它只能传递某种特定的对象,这样就会出现各种List类型,如,StringList, EmployeeList, AddressList等。
add通过Object传递能带来好处,现在我们考虑get函数(返回List中的一个元素).如下是JDK 5之前版本的定义:

  1. public java.lang.Object get(int index)throws IndexOutOfBoundsException

get返回一个Object.不幸的事情从此开始了.假如你储存了两个String对象在一个List中:

  1. List stringList1 =new ArrayList();
  2. stringList1.add("Java 5");
  3. stringList1.add("with generics");

当你想从stringList1取得一个元素时,你得到了一个Object.为了操作原来的类型元素,你不得不把它转换为String。

  1. String s1 = (String) stringList1.get(0);

但是,假如你曾经把一个非String对象加入stringList1中,上面的代码会抛出一个ClassCastException. 有了泛型,你能创建一个单一用途的List实例.比如,你能创建一个只接受String对象的List实例,另外一个实例只能接受Employee对象.这同样适用于集合框架中的其他类型.

泛型入门

像一个函数能接受参数一样,一个泛型也能接受参数.这就是一个泛型经常被称为一个参数化类型的原因.但是不像函数用()传递参数,泛型是用<>传递参数的.声明一个泛型和声明一个普通类没有什么区别,只不过你把泛型的变量放在<>中.
比如,在JDK 5中,你可以这样声明一个java.util.List : List myList;
E 称为类型变量.意味着一个变量将被一个类型替代.替代类型变量的值将被当作参数或返回类型.对于List接口来说,当一个实例被创建以后,E 将被当作一个add或别的函数的参数.E 也会使get或别的参数的返回值.下面是add和get的定义:

  1. boolean add E get(int index)

一个泛型在声明或例示时允许你传递特定的类型变量: E.除此之外,如果E是个类,你可以传递子类;如果E是个接口,你可以传递实现接口的类;

  1. List numberList=new ArrayList();
  2. numberList.add(2.0);
  3. numberList.add(2);

-----------------------------译者添加--------------------

那么mylist的add函数将接受一个String作为他的参数,而get函数将返回一个String.因为返回了一个特定的类型,所以不用类型转化了。

根据惯例,我们使用一个唯一的大写字目表示一个类型变量。为了创建一个泛型,你需在声明时传递同样的参数列表。比如,你要想创建一个ArrayList来操作String ,你必须把String放在<>中。如:

  1. List myList =new ArrayList();

再比如,java.util.Map 是这么定义的:

public interface Map

K用来声明map键(KEY)的类型而V用来表示值(VALUE)的类型。put和values是这么定义的:

V put(K key, V value)
Collection values()

一个泛型不准直接的或间接的是java.lang.Throwable的子类。因为异常是在运行时抛出的,所以它不可能预言什么类型的异常将在编译时抛出.
列表1的例子将比较List在JDK 1.4 和JDK1.5的不同

  1. publicclass GenericListTest {
  2. publicstaticvoid main(String[] args) {
  3. // in JDK 1.4
  4. List stringList1 =new ArrayList();
  5. stringList1.add("Java 1.0 - 5.0");
  6. stringList1.add("without generics");
  7. // cast to java.lang.String
  8. String s1 = (String) stringList1.get(0);
  9. System.out.println(s1.toUpperCase());
  10. // now with generics in JDK 5
  11. List stringList2 =new ArrayList();
  12. stringList2.add("Java 5.0");
  13. stringList2.add("with generics");
  14. // no need for type casting
  15. String s2 = stringList2.get(0);
  16. System.out.println(s2.toUpperCase());
  17. }
  18. }

在列表1中,stringList2是个泛型。声明List告诉编译器List的实例能接受一个String对象。当然,在另外的情况中,你能新建能接受各种对象的List实例。注意,当从List实例中返回成员元素时,不需要对象转化,因为他返回的了你想要的类型,也就是String.

泛型的类型检查(type checking)是在编译时完成的.
最让人感兴趣的事情是,一个泛型是个类型并且能被当作一个类型变量。比如,你想你的List储存lists of Strings,你能通过把List作为他的类型变量来声明List。比如:

  1. List> myListOfListsOfStrings;

要从myList中的第一个List重新取得String,你可以这么用:

  1. String s = myListOfListsOfStrings.get(0).get(0);

下一个列表中的ListOfListsTest类示范了一个List(命名为listOfLists)接受一个String List作为参数。

  1. publicclass ListOfListsTest {
  2. publicstaticvoid main(String[] args) {
  3. List listOfStrings =new ArrayList();
  4. listOfStrings.add("Hello again");
  5. List> listOfLists =new ArrayList>();
  6. listOfLists.add(listOfStrings);
  7. String s = listOfLists.get(0).get(0);
  8. System.out.println(s);// prints "Hello again"
  9. }
  10. }


另外,一个泛型接受一个或多个类型变量。比如,java.util.Map有两个类型变量s。第一个定义了键(key)的类型,第二个定义了值(value)的类型。下面的例子讲教我们如何使用个一个泛型Map.

  1. publicclass MapTest {
  2. publicstaticvoid main(String[] args) {
  3. Map map =new HashMap();
  4. map.put("key1","value1");
  5. map.put("key2","value2");
  6. String value1 = map.get("key1");
  7. }
  8. }


在这个例子中,重新得到一个key1代表的String值,我们不需要任何类型转换。



-----------------------------译者添加--------------------

如果你传递一个String给一个List,比如:

List myList;[@more@]
本文题目:JDK5.0新特性的学习--泛型(上)
本文URL:http://pwwzsj.com/article/gdjpod.html