JAVA中泛型和类型安全的容器

来源:互联网 发布:荷兰国旗问题算法 编辑:程序博客网 时间:2024/06/05 20:42

在JAVA中,同样具备同C++那样方便的容器,比如ArrayList,它可以被当做“可以自动扩充自身尺寸的数组”来使用。看一段代码:

package access;import java.util.*;class Apple{private static long counter;private final long id = counter++;public long id(){return id;}}class Orange{}public class ApplesAndOrangesWithoutGenerics {@SuppressWarnings("unchecked")public static void main(String[] args) {// TODO Auto-generated method stubArrayList apples = new ArrayList();for(int i =0;i < 3; i++)apples.add(new Apple());apples.add(new Orange());for(int i = 0 ; i < apples.size(); i++)((Apple)apples.get(i)).id();}}
上述例子并未使用泛型,Apple和Orange类是有区别的,它们都继承自Object,由于ArrayList保存的是Object,因此我们不仅可以添加Apple对象,还可以添加Orange对象,但是当我们从ArrayList中取出来我们认为的Apple对象时,我们得到的仅仅是Object的引用,必须将其转换为Apple,所以最后需要一对()来括起来,另外在Orange进行强制转换时会发生错误,所以我们应当使用泛型,修改上述代码:

package access;import java.util.*;class Apple{private static long counter;private final long id = counter++;public long id(){return id;}}class Orange{}public class ApplesAndOrangesWithGenerics {public static void main(String[] args) {// TODO Auto-generated method stubArrayList<Apple> apples = new ArrayList<Apple>();for(int i = 0; i < 3; i++ )apples.add(new Apple());for(int i = 0; i < apples.size(); i++)System.out.println(apples.get(i).id());for(Apple c : apples)System.out.println(c.id());}}
此程序运行结果为:


现在,编译器将阻止我们将Orange放入ArrayList中,并且在元素从ArrayList中取出时,类型转换也不再需要,因为ArrayList知道保存的类型,它会在调用get()时自动为我们转型。

而当我们指定了某个类型作为泛型参数时,并不仅限于只能将确切的此类型的对象放置到容器中,向上转型也同样可以像作用于其他类型一样作用于泛型,看如下一段代码:

package access;import java.util.*;class Apple{private static long counter;private final long id = counter++;public long id(){return id;}}class GrannySmith extends Apple{}class Gala extends Apple{}class Fuji extends Apple{}class Braeburn extends Apple{}public class GenericsAndUpcasting {public static void main(String[] args) {// TODO Auto-generated method stubArrayList<Apple> apples = new ArrayList<Apple>();apples.add(new GrannySmith());apples.add(new Gala());apples.add(new Fuji());apples.add(new Braeburn());for(Apple c : apples)System.out.println(c);}}
此程序的输出结果为:


该程序的输出是从Object默认的toString方法产生的,该方法会打印类名,后跟该对象的散列码的无符号十六进制表示,其中散列码通过hashCode方法产生。

JAVA容器类类库的作用是“保存对象”,并将其划分为两个概念:

1.Collection:一个独立元素的序列,这些元素服从一条或多条规则。List必须按照插入的顺序保存元素;Set不能有重复的元素。Quene按照排队规则确定对象产生的顺序,通常情况下与它们被插入的顺序相同。

2.Map:一组成对的“键值对”对象,允许我们按照键来查找值。ArrayList允许我们使用数字来查找值,它将数字与对象关联在一起。映射表允许我们使用另一个对象查找某个对象,被称作关联数组,将某些对象与另一些对象关联在了一起,Map的运用范围非常广泛。

在理想情况下,我们编写的大部分代码都是与这些接口打交道,并且我们唯一需要指定所使用的精确类型的地方在创建的时候:

List<Apple> apples = new ArrayList<Apple>();

此时ArrayList被上转型为List,使用接口的目的在于如果我们决定去修改我们的实现,我们只需在创建出修改它:

List<Apple> apples = new linkedList<Apple>();

因此我们应该创建一个具体类的对象,将其转型为对应的接口,然后在其余的代码中使用此接口。

但由于某些类有特定的方法和功能,比如LinkedList有在List中未包含的额外方法,而TreeMap也有在Map接口中未包含的额外方法,如果我们需要使用到这些方法,就不能上转为通用接口。

Collection接口概况了序列的概念:一种存放一组对象的方式。看下面一段代码:

package access;import java.util.*;public class SimpleCollection {public static void main(String[] args) {// TODO Auto-generated method stubCollection<Integer> c = new ArrayList<Integer>();for(int i = 0; i < 10; i++){c.add(i);}for(Integer i : c)System.out.print(i + ", ");}}
此程序的输出结果为:


任何继承自Collection的类的对象均可正常工作,ArrayList是最基本的的序列类型。所有的Collection也都可以用foreach语法进行遍历操作。在ArrayList中add方法只是添加,而在Set中需要考虑到元素重复的问题。

原创粉丝点击