Chinaunix首页 | 论坛 | 博客
  • 博客访问: 755043
  • 博文数量: 130
  • 博客积分: 2951
  • 博客等级: 少校
  • 技术积分: 1875
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-04 18:32
文章分类

全部博文(130)

文章存档

2013年(1)

2012年(129)

分类: Java

2012-02-29 09:24:48

1. 泛型的好处
  • 类型安全
  • 消除强类型转换
  • 潜在的性能收益
2. 示例
  1. public interface Map<K, V> {
  2.   public void put(K key, V value);
  3.   public V get(K key);
  4. }

  5. Map<String, String> map = new HashMap<String, String>();
3. 关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List 不是 List 的父类型。

假设您具有该方法:

void printList(List l) {
     for (Object o : l)
         System.out.println(o);
 }

上面的代码在 JDK 5.0 上编译通过,但是如果试图用 List 调用它,则会得到警告。出现警告是因为,您将泛型(List)传递给一个只承诺将它当作 List(所谓的原始类型)的方法,这将破坏使用泛型的类型安全。

如果试图编写像下面这样的方法,那么将会怎么样?

void printList(List l) {
         for (Object o : l)
               System.out.println(o);
 }

它仍然不会通过编译,因为一个 List 不是 一个 List (正如前一屏 泛型不是协变的 中所学的)。这才真正烦人 —— 现在您的泛型版本还没有普通的非泛型版本有用!

解决方案是使用类型通配符

void printList(List l) {
          for (Object o : l)
               System.out.println(o);
 }

上面代码中的问号是一个类型通配符。它读作“问号”。List 是任何泛型 List 的父类型,所以您完全可以将List、List 或 List>> 传递给 printList()。

4. 类型通配符,这让您可以声明 List 类型的变量。您可以对这样的 List 做什么呢?非常方便,可以从中检索元素,但是不能添加元素。原因不是编译器知道哪些方法修改列表哪些方法不修改列表,而是(大多数)变化的方法比不变化的方法需要更多的类型信息。下面的代码则工作得很好:
 
List li = new ArrayList();
li.add(new Integer(42));
List lu = li;
System.out.println(lu.get(0));

为什么该代码能工作呢?对于 lu,编译器一点都不知道 List 的类型参数的值。但是编译器比较聪明,它可以做一些类型推理。在本例中,它推断未知的类型参数必须扩展 Object 。(这个特定的推理没有太大的跳跃,但是编译器可以作出一些非常令人佩服的类型推理,后面就会看到(在 底层细节 一节中)。所以它让您调用 List.get() 并推断返回类型为 Object。

另一方面,下面的代码不能工作:

List li = new ArrayList();
li.add(new Integer(42));
List lu = li;
lu.add(new Integer(43)); // error

在本例中,对于 lu,编译器不能对 List 的类型参数作出足够严密的推理,以确定将 Integer 传递给 List.add() 是类型安全的。所以编译器将不允许您这么做。

以免您仍然认为编译器知道哪些方法更改列表的内容哪些不更改列表内容,请注意下面的代码将能工作,因为它不依赖于编译器必须知道关于 lu 的类型参数的任何信息:

List li = new ArrayList();
li.add(new Integer(42));
List lu = li;
lu.clear();

5. 方法也可以被泛型化,不管它们定义在其中的类是不是泛型化的。

声明泛型方法,一般是因为您想要在该方法的多个参数之间宣称一个类型约束。例如,下面代码中的 ifThenElse()方法,根据它的第一个参数的布尔值,它将返回第二个或第三个参数:

public T ifThenElse(boolean b, T first, T second) {
      return b ? first : second;
}

注意,您可以调用 ifThenElse(),而不用显式地告诉编译器,您想要 T 的什么值。编译器不必显式地被告知 T 将具有什么值;它只知道这些值都必须相同。编译器允许您调用下面的代码,因为编译器可以使用类型推理来推断出,替代 T 的 String 满足所有的类型约束:

String s = ifThenElse(b, "a", "b");

类似地,您可以调用:

Integer i = ifThenElse(b, new Integer(1), new Integer(2));

但是,编译器不允许下面的代码,因为没有类型会满足所需的类型约束:

String s = ifThenElse(b, "pi", new Float(3.14));

阅读(1777) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~