首页 8. 尚硅谷_Java基础_泛型

8. 尚硅谷_Java基础_泛型

举报
开通vip

8. 尚硅谷_Java基础_泛型 泛 型 1. 介绍 下面是那种典型用法: List myIntList = new ArrayList();// 1 myIntList.add(new Integer(0));// 2 Integer x = (Integer) myIntList.iterator().next();// 3 第 3 行的类型转换有些烦人。通常情况下,程序员知道一个特定的 list 里边放的是什么类型的数据。但是,这个类型转换...

8. 尚硅谷_Java基础_泛型
泛 型 1. 介绍 下面是那种典型用法: List myIntList = new ArrayList();// 1 myIntList.add(new Integer(0));// 2 Integer x = (Integer) myIntList.iterator().next();// 3 第 3 行的类型转换有些烦人。通常情况下,程序员知道一个特定的 list 里边放的是什么类型的数据。但是,这个类型转换是必须的(essential)。编 译器只能保证 iterator 返回的是 Object 类型。为了保证对 Integer 类型变量 赋值的类型安全,必须进行类型转换。 当然,这个类型转换不仅仅带来了混乱,它还可能产生一个运行时错误 (run time error),因为程序员可能会犯错。 程序员如何才能明确表示他们的意图,把一个 list(集合) 中的内容限制 为一个特定的数据类型呢?这就是 generics 背后的核心思想。这是上面程 序片断的一个泛型版本: List myIntList = new ArrayList(); // 1 myIntList.add(new Integer(0)); // 2 Integer x = myIntList.iterator().next(); // 3 注意变量 myIntList 的类型声明。它指定这不是一个任意的 List,而是 一个 Integer 的 List,写作:List。我们说 List 是一个带一个类型 参数的泛型接口(a generic interface that takes a type parameter),本 例中,类型参数是 Integer。我们在创建这个 List 对象的时候也指定了一个 类型参数。 另一个需要注意的是第 3 行没了类型转换。 现在,你可能认为我们已经成功地去掉了程序里的混乱。我们用第 1 行的类型参数取代了第 3 行的类型转换。然而,这里还有个很大的不同。 编译器现在能够在编译时检查程序的正确性。当我们说 myIntList 被声明为 List类型,这告诉我们无论何时何地使用 myIntList 变量,编译器 保证其中的元素的正确的类型。 实际结果是,这可以增加可读性和稳定性(robustness),尤其在大型的 程序中。 2. 定义简单的泛型 下面是从 java.util 包中的 List 接口和 Iterator 接口的定义中摘录的片断: public interface List { void add(E x); Iterator iterator(); } public interface Iterator { 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 被全部替换成 Integer 的版本: public interface IntegerList { void add(Integer x) Iterator iterator(); } 类型参数就跟在方法或构造函数中普通的参数一样。就像一个方法有形 式参数(formal value parameters)来描述它操作的参数的种类一样,一个 泛型声明也有形式类型参数(formal type parameters)。当一个方法被调用, 实参(actual arguments)替换形参,方法体被执行。当一个泛型声明被调用, 实际类型参数(actual type arguments)取代形式类型参数。 一个命名的习惯:推荐用简练的名字作为形式类型参数的名字(如果可 能,单个字符)。最好避免小写字母 3. 泛型和子类继承 让我们测试一下我们对泛型的理解。下面的代码片断合法么? List ls = new ArrayList(); //1 List lo = ls; //2 第 1 行当然合法,但是这个问题的狡猾之处在于第 2 行。 这产生一个问题: 一个 String 的 List 是一个 Object 的 List 么?大多数人的直觉是回答: “当然!”。 好,在看下面的几行: lo.add(new Object()); // 3 String s = ls.get(0); // 4: 试图把 Object 赋值给 String 这里,我们使用 lo 指向 ls。我们通过 lo 来访问 ls,一个 String 的 list。 我们可以插入任意对象进去。结果是 ls 中保存的不再是 String。当我们试 图从中取出元素的时候,会得到意外的结果。 java 编译器当然会阻止这种情况的发生。第 2 行会导致一个编译错误。 总之,如果 Foo 是 Bar 的一个子类型(子类或者子接口),而 G 是某种 泛型声明,那么 G是 G的子类型并不成立!! 为了处理这种情况,考虑一些更灵活的泛型类型很有用。到现在为止我 们看到的规则限制比较大。 4. 通配符(Wildcards) 考虑写一个例程来打印一个集合(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 c) { for (Object e : c) { System.out.println(e); } } 问题是新版本的用处比老版本小多了。老版本的代码可以使用任何类型 的 Collection 作为参数,而新版本则只能使用 Collection,我们刚 才阐述了,它不是所有类型的 collections 的父类。 那么什么是各种 collections 的父类呢?它写作: Collection(发音 为:"collection of unknown"),就是,一个集合,它的元素类型可以匹配任 何类型。显然,它被称为通配符。我们可以写: void printCollection(Collection c) { for (Object e : c) { System.out.println(e); } } 现在,我们可以使用任何类型的 collection 来调用它。注意,我们仍然 可以读取 c 中的元素,其类型是 Object。这永远是安全的,因为不管 colle ction 的真实类型是什么,它包含的都是 Object。 但是将任意元素加入到其中不是类型安全的: Collection c = new ArrayList(); c.add(new Object()); // 编译时错误 因为我们不知道 c 的元素类型,我们不能向其中添加对象。 add 方法有类型参数 E 作为集合的元素类型。我们传给 add 的任何参 数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我 们无法传任何东西进去。唯一的例外是 null,它是所有类型的成员。 另一方面,我们可以调用 get()方法并使用其返回值。返回值是一个未 知的类型,但是我们知道,它总是一个 Object 4.1. 有限制的通配符(Bounded Wildcards) 考虑一个简单的画图程序,它可以用来画各种形状,比如矩形和圆形。 为了在程序中表示这些形状,你可以定义下面的类继承结构: public abstract class Shape { public abstract void draw(Canvas c); } public class Circle extends Shape { private int x, y, radius; public void draw(Canvas c) { // ... } } public class Rectangle extends Shape { private int x, y, width, height; public void draw(Canvas c) { // ... } } 这些类可以在一个画布(Canvas)上被画出来: public class Canvas { public void draw(Shape s) { s.draw(this); } } 所有的图形通常都有很多个形状。假定它们用一个 list 来表示,Canva s 里有一个方法来画出所有的形状会比较方便: public void drawAll(List shapes) { for (Shape s : shapes) { s.draw(this); } } 现在,类型规则导致 drawAll()只能使用 Shape 的 list 来调用。它不能, 比如说对 List来调用。这很不幸,因为这个方法所作的只是从这个 list 读取 shape,因此它应该也能对 List调用。我们真正要的是这 个方法能够接受一个任意种类的 Shape: public void drawAll(List shapes) { //..} 这里有一处很小但是很重要的不同:我们把类型 List 替换成 了 List。现在 drawAll()可以接受任何 Shape 的子类的 List,所以我们可以对 List进行调用。 List是有限制通配符的一个例子。这里?代表一个 未知的类型,就像我们前面看到的通配符一样。但是,在这里,我们知道 这个未知的类型实际上是 Shape 的一个子类。我们说 Shape 是这个通配符 的上限(upper bound)。 像平常一样,要得到使用通配符的灵活性有些代价。这个代价是,现在 向 shapes 中写入是非法的。比如下面的代码是不允许的: public void addRectangle(List shapes) { shapes.add(0, new Rectangle()); // compile-time error! } 你应该能够指出为什么上面的代码是不允许的。因为 shapes.add 的第 二个参数类型是 ? extends Shape ——一个 Shape 未知的子类。因此我 们不知道这个类型是什么,我们不知道它是不是 Rectangle 的父类;它可 能是也可能不是一个父类,所以这里传递一个 Rectangle 不安全。 5. 泛型方法 考虑写一个方法,它用一个 Object 的数组和一个 collection 作为参数, 完成把数组中所有 object 放入 collection 中的功能。 下面是第一次尝试: static void fromArrayToCollection(Object[] a, Collection c) { for (Object o : a) { c.add(o); // 编译期错误 } } 现在,你应该能够学会避免初学者试图使用 Collection作为集 合参数类型的错误了。或许你已经意识到使用 Collection也不能工作。 回忆一下,你不能把对象放进一个未知类型的集合中去。 解决这个问题的办法是使用 generic methods。就像类型声明,方法的 声明也可以被泛型化——就是说,带有一个或者多个类型参数。 static void fromArrayToCollection(T[] a, Collection c){ for (T o : a) { c.add(o); // correct } } 我们可以使用任意集合来调用这个方法,只要其元素的类型是数组的元 素类型的父类。 Object[] oa = new Object[100]; Collection co = new ArrayList(); fromArrayToCollection(oa, co);// T 指 Object String[] sa = new String[100]; Collection cs = new ArrayList(); fromArrayToCollection(sa, cs);// T inferred to be String fromArrayToCollection(sa, co);// T inferred to be Object Integer[] ia = new Integer[100]; Float[] fa = new Float[100]; Number[] na = new Number[100]; Collection cn = new ArrayList(); fromArrayToCollection(ia, cn);// T inferred to be Number fromArrayToCollection(fa, cn);// T inferred to be Number fromArrayToCollection(na, cn);// T inferred to be Number fromArrayToCollection(na, co);// T inferred to be Object fromArrayToCollection(na, cs);// compile-time error 注意,我们并没有传送真实类型参数(actual type argument)给一个泛 型方法。编译器根据实参为我们推断类型参数的值。它通常推断出能使调 用类型正确的最明确的类型参数。
本文档为【8. 尚硅谷_Java基础_泛型】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_941844
暂无简介~
格式:pdf
大小:1MB
软件:PDF阅读器
页数:0
分类:互联网
上传时间:2013-09-03
浏览量:14