《徐徐道来话Java》:泛型的基本概念(1)

来源:互联网 发布:手机淘宝用什么付账 编辑:程序博客网 时间:2024/05/21 06:56

《徐徐道来话Java》:泛型的基本概念(1)

泛型是一种编程范式(Programming Paradigm),是为了效率和重用性产生的。由Alexander Stepanov(C++标准库主要设计师)和David Musser(伦斯勒理工学院CS名誉教授)首次提出,自实现始,就成为了ANSI/ISO C++重要标准之一。

Java自1.5版本开始提供泛型,其本质是一个参数化的类型,那么,何谓参数化?

参数是一个外部变量。设想一个方法,其参数的名称和实际的数值是外部传入的,那么,该参数的类型是否也作为一个参数,在运行时决定呢?这就是泛型的作用。参考如下代码:

List<String> list = newArrayList<String>;

list.add(1);

在第2行,会抛出编译期错误。

The method add(int, String) in the type List<String> is not applicable for the arguments (int)

这就是因为,list在声明时定义了String为自己需要的类型,而1是一个整型数。在上面的例子中,以下几种添加方式都是合法的:

list.add("字符串");

list.add(newString);

String str="字符串";

list.add( str);

在J2SE1.5之前的版本中,Java没有办法显式的对容器进行编译期内容限制,在没有注释或者文档说明的情况下,很容易出现运行时错误。下面举一个错误的例子:

ArrayList list = newArrayList;

list.add(0);

list.add(1);

list.add('2');

list.add(3);

//输出list内容

System.out.println(list);

//遍历输出list内容

for(inti = 0, len = list.size; i < len; i++) {

Integer object = (Integer) list.get(i);

System.out.println(object);

}

输出结果如下所示:

[0, 1, 2, 3]

0

1

Exception in thread "main" java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.Integer

at capter3.generic.Generic3_1.test2(Generic3_1.java:28)

at capter3.generic.Generic3_1.main(Generic3_1.java:17)

可以看到,直接输出list的时候,int类型的1和char类型的2是看不出区别的,假设忽略了这点,直接转为Integer来使用的时候,就抛出了强制转化异常。

除了会造成异常外,还可以思考一个问题,如果没有泛型,那么list.get(int)方法返回的始终是个Object,那么要如何在运行时之外确定它的类型呢?

综上,已经证明了泛型存在的必要性,它提供了以下能力:

1、避免代码中的强制类型转换;

2、限定类型,在编译时提供一个额外的类型检查,避免错误的值被存入容器;

3、实现一些特别编程技巧。比如:提供一个方法用于拷贝对象,在不提供额外方法参数的情况下,使返回值类型和方法参数类型保持一致。

1.1.1 泛型的分类

根据泛型使用方式的不同,可分为泛型接口、泛型类和泛型方法。它们的定义如下:

1、泛型接口:在接口定义的接口名后加上<泛型参数名>,就定义了一个泛型接口,该泛型参数名的作用域存在于接口定义和整个接口主体之内;

2、泛型类:在类定义的类名后加上<泛型参数名>,就定义了一个泛型类,该泛型参数名的作用域存在于类定义和整个类主体之内;

3、方法类:在方法的返回值之前加上<泛型参数名>,就定义了一个泛型方法,该泛型参数名的作用域包括方法返回值,方法参数,方法异常,以及整个方法主体。

下面通过一个例子来分别介绍这几种泛型的定义方法,示例代码如下:

/**

* 在普通的接口后加上<泛型参数名>即可以定义泛型接口

*/

interfaceGenericInterface<T> {

* 在类定义后加上<泛型参数名>即可定义一个泛型类,注意后面这个GenericInterface<T>,这里是使用类的泛型参数,而非定义。

*/

classGenericClass<T>implementsGenericInterface<T>{

/**

* 在返回值前定义了泛型参数的方法,就是泛型方法。

*/

public<K, EextendsException> K genericMethod(K param)throwsE {

java.util.List<K> list = newArrayList<K>;

K k = null;

returnnull;

在上例中,class GenericClass<T> implements GenericInterface<T>中有两个地方使用了<T>,它们是同一个概念吗?为了回答这个问题,下面给出几个基本概念,通过对这些基本概念的掌握,将可以解决大部分类似的泛型问题。

a、类(接口)的泛型定义位置紧接在类(接口)定义之后,可以替代该类(接口)定义内部的任意类型。在该类(接口)被声明时,确定泛型参数。

b、方法的泛型定义位置在修饰符之后返回值之前,可以替代该方法中使用的任意类型,包括返回值、参数以及局部变量。在该方法被调用时,确定泛型参数,一般来说,是通过方法参数来确定的泛型参数。

c、<>的出现有两种情况,一是定义泛型,二是使用某个类\接口来具象化泛型。

根据上面介绍的几个基本概念,再来分析class GenericClass<T> implemenets GenericInterface<T>这句代码。可知,class GenericClass是类的定义,那么第一个<T>就构成了泛型参数的定义,而接口GenericInterface是定义在别处的,该代码位置是对此接口的引用,所以,第二个<T>则是使用泛型T来规范GenericInterface。

引申:

如果泛型方法是没有形参的,那么是否还有其它方法来指定类型参数?

答案:有方法指定,但是这个语法并不常见,实现代码如下:

GenericClass<String> gc=newGenericClass<String>;

gc.<String>genericMethod(null);

可以看到这里出现一个很特别的代码形式,gc.genericMethod(null)中间多出了一个<String>,这就是为genericMethod方法进行泛型参数定义了。

0 0
原创粉丝点击