在Java SE1.5中,增加了一个新的特性:泛型(日本语中的总称型)。何谓泛型呢?通俗的说,就是泛泛的指定对象所操作的类型,而不像常规方式一样使用某种固定的类型去指定。泛型的本质就是将所操作的数据类型参数化,也就是说,该数据类型被指定为一个参数。这种参数类型可以使用在类、接口以及方法定义中。
一、为什么使用泛型呢?
在以往的J2SE中,没有泛型的情况下,通常是使用Object类型来进行多种类型数据的操作。这个时候操作最多的就是针对该Object进行数据的强制转换,而这种转换是基于html" target="_blank">开发者对该数据类型明确的情况下进行的(比如将Object型转换为String型)。倘若类型不一致,编译器在编译过程中不会报错,但在运行时会出错。
使用泛型的好处在于,它在编译的时候进行类型安全检查,并且在运行时所有的转换都是强制的,隐式的,大大提高了代码的重用率。
二、定义&使用
类型参数的命名风格为:
推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通
的形式参数很容易被区分开来。
使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们
可能使用字母表中T的临近的字母,比如S。
如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混
淆。对内部类也是同样。
1.定义带类型参数的类
在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取
值范围进行限定,多个类型参数之间用,号分隔。
定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,
就像使用普通的类型一样。
注意,父类定义的类型参数不能被子类继承。
public class TestClassDefine<T, S extends T> { .... }
2.定义待类型参数方法
在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,
同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。
定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。
例如:
public <T, S extends T> T testGenericMethodDefine(T t, S s){ ... }
注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继
承关系, 返回值的类型和第一个类型参数的值相同。
如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。
public <T> void testGenericMethodDefine2(List<T> s){ ... }
应改为
public void testGenericMethodDefine2(List<?> s){ ... }
3. 类型参数赋值
当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。
4.对带类型参数的类进行类型参数赋值
对带类型参数的类进行类型参数赋值有两种方式
第一声明类变量或者实例化时。例如
List<String> list; list = new ArrayList<String>;
第二继承类或者实现接口时。例如
public class MyList<E> extends ArrayList<E> implements List<E> {...}
5.对带类型参数方法进行赋值
当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如
public <T> T testGenericMethodDefine3(T t, List<T> list){ ... } public <T> T testGenericMethodDefine4(List<T> list1, List<T> list2){ ... } Number n = null; Integer i = null; Object o = null; testGenericMethodDefine(n, i);//此时T为Number, S为Integer testGenericMethodDefine(o, i);//T为Object, S为Integer List<Number> list1 = null; testGenericMethodDefine3(i, list1)//此时T为Number List<Integer> list2 = null; testGenericMethodDefine4(list1, list2)//编译报错
6.通配符
在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如
List<?> unknownList; List<? extends Number> unknownNumberList; List<? super Integer> unknownBaseLineIntgerList;
注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,
因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL
List<String> listString; List<?> unknownList2 = listString; unknownList = unknownList2; listString = unknownList;//编译错误
7.数组范型
可以使用带范型参数值的类声明数组,却不可有创建数组
List<Integer>[] iListArray; new ArrayList<Integer>[10];//编译时错误
三、扩展
1、extends语句
使用extends语句将限制泛型参数的适用范围。例如:
<T extends collection> ,则表示该泛型参数的使用范围是所有实现了collection接口的calss。如果传入一个<String>则程序编译出错。
2、super语句
super语句的作用与extends一样,都是限制泛型参数的适用范围。区别在于,super是限制泛型参数只能是指定该class的上层父类。
例如<T super List>,表示该泛型参数只能是List和List的上层父类。
3、通配符
使用通配符的目的是为了解决泛型参数被限制死了不能动态根据实例来确定的缺点。
举个例子:public class SampleClass < T extends S> {…}
假如A,B,C,…Z这26个class都实现了S接口。我们使用时需要使用到这26个class类型的泛型参数。那实例化的时候怎么办呢?依次写下
SampleClass<A> a = new SampleClass(); SampleClass<B> a = new SampleClass(); … SampleClass<Z> a = new SampleClass();
这显然很冗余,还不如使用Object而不使用泛型,呵呵,是吧?
别着急,咱们使用通配符,就OK了。
SampleClass<? Extends S> sc = new SampleClass();
只需要声明一个sc变量,很方便把!
这可能是一个愚蠢的问题,所以请原谅我的无知。 如果我用: 编译器说foo.execute(val)是一个错误。带有一条沿着setWrapped方法(bar )行的消息,类型为foo 不适用于参数(bar )。 如果我想把福变成
你能向我解释一下这里发生了什么,为什么它没有错误。 很抱歉,如果已经有类似的问题,我没有正确地表达这个问题,所以不能用谷歌搜索它
问题内容: 因此,我正在开发这个通用的HashTable类,并且希望将其通用地用于任何数量的传入类型,并且我还想将内部存储数组初始化为LinkedList的数组(出于冲突目的),其中为确保类型安全,每个LinkedList都被预先指定为HashTable类中的泛型类型。我该怎么做?以下代码最能阐明我的意图,但当然不会编译。 问题答案: Java中的泛型不允许创建具有泛型类型的数组。您可以将数组转换
问题内容: 众所周知,arraylist是init。应该是这样的 那么,为什么Java允许这些? 那么,如果它们是正确的,为什么不允许这些? 编译器消息是:ArrayList类型的方法add(int,capture#1-of?extended Object)不适用于参数(int) 更一般 我读到了这件事,但很高兴收到您的来信。谢谢 另一个有趣的地方是: 问题答案: 您不能为类型的引用分配,因为它允
问题内容: 我有一个代表文本片段的泛型类。该文本片段可能具有多种不同模式(突出显示的不同类型)中的任何一种。这些模式用枚举表示。每个项目的Enum可能不同,但是它必须实现一个接口,该接口提供了一种将其中两个结合的方法(可以突出显示并加粗显示)。所以我有一个界面: 然后我的TextFragment是文本字符串和模式的容器。但是当我尝试声明该类时: 我收到以下错误: 令牌“扩展”的语法错误,预期 根据
本文向大家介绍Java泛型详解,包括了Java泛型详解的使用技巧和注意事项,需要的朋友参考一下 1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象。然而,过了一阵,我们想要实现一个大小可以改变的Date对象数组,这时我们当然希望能够重用之前写过的那个针对String对象的Arra