您的位置:首页 > 编程语言 > Java开发

Java核心技术第12章(1)

2015-10-20 16:06 519 查看

第12章  泛型程序设计

    使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性.泛型对于集合类尤其有用.

    至少在表面上看来,泛型很像C++中的模板.与Java一样,在C++中,模板也是最先被添加到语言中支持强类型集合的.

12.1    为什么要使用泛型程序设计

    泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用.例如,我们并不希望为聚集String和File对象分别设计不同的类.实际上,也不需要这样做,因为一个ArrayList类可以聚集任何类型的对象.这是一个泛型程序设计的实例.

    在Java中增加泛型类之前,泛型程序设计是用继承实现的.ArrayList类只维护一个Object引用的数组:
public class ArrayList  // before generic classes
{
private Object[] elementData;
...
public Object get(int i) { ... }
public void add(Object o){ ... }
}
    这样实现有两个问题.当获取一个值时必须进行强制类型转换.
ArrayList files = new ArrayList();
...
String filename = (String) files.get(0);
    此外,这里没有错误检查.可以向数组列表中添加任何类的对象.
files.add(new File("..."));
    对于这个调用,编译和运行都不会出错.然而在其他地方,如果将get的结果强制类型转换为String类型,就会产生一个错误.

    泛型提供了一个更好的解决方法:类型参数(type parameters).
ArrayList类有一个类型参数用来指示元素的类型:
ArrayList<String> files = new ArrayList<String>();
    这使得代码具有更好的可读性.

    注释:前面已经提到,在Java SE7及以后的版本中,构造函数可以省略泛型类型:
ArrayList<String> files = new ArrayList<>();
    省略的类型可以从变量的类型推断得出.

    编译器也可以
很好地利用这个信息.当调用get的时候,不需要进行强制类型转换,编译器就知道返回类型为String,而不是Object:
String filename = files.get(0);
    编译器还知道ArrayList<String>中的add方法有一个类型为String的参数,这将比使用Object类型的参数安全一些.现在,编译器可以进行检查,避免插入错误类型的对象.例如:
files.add(new File("...")); // can only add String object to ArrayList<String>
    是无法通过编译的.出现编译错误比类在运行时出现类的强制类型转换异常要好得多.类型参数的魅力在于:使得程序具有更好的可读性和安全性.
    那些原本涉及许多来自自通用类型(如Object或Comparable接口)的强制类型转换的代码一定会因使用类型参数而受益.

12.2    定义简单泛型类

    一个泛型类(generic class)就是具有一个或多个类型变量的类.下面是Pair类的代码:
public class Pair<T>
{
private T first;
private T second;

public Pair(){ first = null; second = null;}
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
    Pair类引入了一个类型变量T,用尖括号<>括起来,并放在类名的后面.泛型类可以有多个类型变量.例如,可以定义Pair类,其中第一个域和第二个域使用不同的类型:
public class Pair<T, U> { ... }
    类定义中的类型变量指定方法的返回类型以及域和局部变量的类型.例如:
private T first;    // uses the type variable
    注释:类型变量使用大写形式,且比较短,这是很常见的.在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型. T(需要时还可以用临近的字母U和S)表示"任意类型".

    用具体的类型替换类型变量就可以实例化泛型类型,例如:
Pair<String>
    可以将结果想象成带有构造器的普通类:
Pair<String>()
Pair<String>(String, String)
    和方法:
String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)
    换句话说,泛型类可以看做普通类的工厂.

    程序12-1使用了Pair类.静态的minmax方法遍历了数组并同时计算出最小值和最大值.它用一个Pair对象返回了两个结果.

    注释:从表面上看,Java的泛型类类似于C++的模板类.唯一明显的不同是Java没有专用的 template 关键字.但是,在本章中将看到,这两种机制有着本质的区别.

    pair1/PairTest1.java如下所示:
package pair1;

public class PairTest1
{
public static void main(String[] args)
{
String[] words = {"Mary", "had", "a", "little", "lamb"};
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}

class ArrayAlg
{
/**
* Gets the minimum and maximum of an array of strings
* @param a an array of strings
* @return a pair with the min and max value, or null if a is null or empty
*/
public static Pair<String> minmax(String[] a)
{
if (a == null || a.length == 0)
return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}
    运行结果如下所示:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: