首页 > 编程语言 > 详细

JAVA泛型

时间:2015-03-22 16:39:00      阅读:128      评论:0      收藏:0      [点我收藏+]

介绍

下面是那种典型用法:

List myIntList = new ArrayList();// 1
myIntList.add(new Integer(0));// 2
Integer x = (Integer) myIntList.iterator().next();// 3

第 3 行的类型转换有些烦人。通常情况下,程序员知道一个特定的 list
里边放的是什么类型的数据。但是,这个类型转换是必须的(essential)。 编
译器只能保证 r iterator 返回的是 t Object 类型。 为了保证对 r Integer 类型变量
赋值的类型安全,必须进行类型转换。
当然,这个类型转换不仅仅带来了混乱,它还可能产生一个运行时错误
(run time error),因为程序员可能会犯错。
程序员如何才能明确表示他们的意图, 把一个 list( ( 集合) 中的内容限制
为一个特定的数据类型呢?这就是 generics 背后的核心思想。这是上面程
序片断的一个泛型版本:

List<Integer> myIntList = new ArrayList<Integer>(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = myIntList.iterator().next(); // 3

注意变量 myIntList 的类型声明。它指定这不是一个任意的 List,而是
一个 Integer 的 List,写作:List。我们说 t List 是一个带一个类型
参数的泛型接口(a generic interface that takes a type parameter),本
例中,类型参数是 Integer。我们在创建这个 List 对象的时候也指定了一个
类型参数。
另一个需要注意的是第 3 行没了类型转换。
现在,你可能认为我们已经成功地去掉了程序里的混乱。我们用第 1
行的类型参数取代了第 3 行的类型转换。然而,这里还有个很大的不同。
编译器现在能够在编译时检查程序的正确性。当我们说 myIntList 被声明为
List类型,这告诉我们无论何时何地使用 myIntList 变量,编译器
保证其中的元素的正确的类型。
实际结果是,这可以增加可读性和稳定性(robustness),尤其在大型的
程序中。

定义简单的泛型

下面是从java.util包中的List接口和Iterator接口的定义中摘录的片断:

public interface List<E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}

这些都应该是很熟悉的,除了尖括号中的部分,那是接口 List 和 Iterat
or 中的形式类型参数的声明(the declarations of the formal type param
eters of the interfaces List and Iterator)。
类型参数在整个类的声明中可用, 几乎是所有可以使用其他普通类型的
地方
在介绍那一节我们看到了对泛型类型声明 List (the generic type decl
aration List) 的调用,如 List。在这个调用中(通常称作一个参数
化类型 a parameterized type),所有出现的形式类型参数(formal type pa
rameter,这里是 E)都被替换成实体类型参数(actual type argument)(这里
是 Integer)。
你可能想象, List 代表一个 E E 被全部替换成 r Integer 的版本:

public interface IntegerList {
void add(Integer x)
Iterator<Integer> iterator();
}

类型参数就跟在方法或构造函数中普通的参数一样。 就像一个方法有形
式参数(formal value parameters)来描述它操作的参数的种类一样,一个
泛型声明也有形式类型参数(formal type parameters)。当一个方法被调用,
实参(actual arguments)替换形参, 方法体被执行。 当一个泛型声明被调用,
实际类型参数(actual type arguments)取代形式类型参数。
一个命名的 习惯:推荐用简练的名字作为形式类型参数的名字( ( 如果可
能,单个字符) )

泛型和子类继承

让我们测试一下我们对泛型的理解。下面的代码片断合法么?

    List<String> ls = new ArrayList<String>(); //1
    List<Object> lo = ls; //2

第 1 行当然合法,但是这个问题的狡猾之处在于第 2 行。
这产生一个问题:
一个 g String 的 的 t List 是一个 t Object 的 的 t List 么?大多数人的直觉是回答:
“当然!”。
好,在看下面的几行:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: 试图把 Object 赋值给 String

这里,我们使用 lo 指向 ls。我们通过 lo 来访问 ls,一个 String 的 list。
我们可以插入任意对象进去。结果是 ls 中保存的不再是 String。当我们试
图从中取出元素的时候,会得到意外的结果。
a java 编译器当然会阻止这种情况的发生。 第 2 2 行会导致一个编译错误。
总之, 如果 o Foo 是 是 r Bar 的一个子类型( ( 子类或者子接口) ) ,而 G G 是某种
泛型声明,那么 G是 是 G 的子类型并不成立 !!
为了处理这种情况,考虑一些更灵活的泛型类型很有用。到现在为止我
们看到的规则限制比较大。

通配符

考虑写一个例程来打印一个集合(Collection)中的所有元素。 下面是在老
的语言中你可能写的代码:

void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System. out .println(i.next());
}
}

下面是一个使用泛型的幼稚的尝试(使用了新的循环语法):

void printCollection(Collection<Object> c) {
    for (Object e : c) {
        System. out .println(e);
    }
}

问题是新版本的用处比老版本小多了。 老版本的代码可以使用任何类型
的 的 C Cn ollection 作为参数,而新版本则只能使用 Collection,我们刚
才阐述了,它不是所有类型的 collections 的父类。
那么什么是各种 s collections 的父类呢?它写作: Collection

void printCollection(Collection<?> c) {
    for (Object e : c) {
        System. out .println(e);
    }
}

现在,我们可以使用任何类型的 collection 来调用它。注意,我们 仍然
可以读取 c c 中的元素,其类型是 Object。这永远是安全的,因为不管 colle
ction 的真实类型是什么,它包含的都是 Object。
但是 将任意元素加入到其中不是类型安全的:
Collection

JAVA泛型

原文:http://blog.csdn.net/github_20066005/article/details/44537859

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!