什么是泛型
Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。需要注意的是java泛型是在1.5之后引入的,为了向下兼容java泛型是伪泛型。
泛型的定义
如果定义的一个类或接口有一个或多个类型变量,则可以使用泛型。泛型类型变量由尖括号界定,放在类或接口名的后面,下面定义尖括号中的T称为类型变量。意味着一个变量将被一个类型替代替代类型变量的值将被当作参数或返回类型。下面分别是泛型类、泛型接口的定义:
public class GenericClass<T> {
......
}
interface IGenericClass<T>{
......
}
是否拥有泛型方法,与其所在的类是否泛型无关。要定义泛型方法,只需将泛型参数列表置于返回值前
//泛型方法
public <E>void log( E e){
.......
}
为什么使用泛型
与非泛型代码相比,使用泛型有许多优点
1,在编译时进行更强的类型检查,Java编译器将强类型检查应用于通用代码,如果代码违反类型安
全,则会发出错误。修复编译时错误比修复运行时错误容易,后者可能很难找到。
2,消除类型转换。以下不带泛型的代码段需要强制转换:
List list = new ArrayList();
list.add("hello");
list.add("这里如果放一个对象");
String s = (String) list.get(0);//需要进行强制类型转换
String s = (String) list.get(1);//在进行类型转换时开发期和编译期不会报错但是在使用时就会ClassCastException
当使用泛型重写时,代码不需要强制转换:
List<String> list = new ArrayList<String>();
list.add("hello");
list.add(0);//在添加数据的时候就会报错
String s = list.get(0); // 不需要进行强制转换
3,使程序员能够实现通用算法。通过使用泛型,程序员可以实现对不同类型的集合进行工作,可以自
定义并且类型安全且易于阅读的泛型算法
通配符
在通用代码中,称为通配符的问号( ? )表示未知类型。通配符可以在多种情况下使用:作为参数,字
段或局部变量的类型;有时作为返回类型(尽管更具体的做法是更好的编程习惯)。通配符从不用作泛
型方法调用,泛型类实例创建或超类型的类型参数。
1,上限通配符
你可以使用上限通配符来放宽对变量的限制。例如,假设你要编写一种适用于 List , List 和 List 的
方法;可以使用上限通配符来实现。要声明上限通配符,请使用通配符( ? ),后跟 extends 关键字,然后是其上限。请注意,在这种情况下, extends 通常用于表示“扩展”(如在类中)或“实现”(如在接口中)。上限通配符只能获取值不能修改
例如:要编写在 Number 类型的列表和 Number 的子类型(如 Integer 、 Double 和 Float )上工作的方法,你会指定 List 。术语 List 比 List 更有限制性,因为前者只匹配 Number 类型的列表,而后者匹配
Number 类型的列表或其任何子类。
public static void process(List<? extends Number> list) {
for (Numberelem : list) { // ... }
//***************************
list.add()
}
在 foreach 子句中,elem变量对列表中的每个元素进行迭代。在Number类中定义的任何方法现在都可以在elem上使用。
2,无限通配符
无界通配符类型使用通配符( ? )来指定,例如 List 。这就是所谓的未知类型的列表。有两种情况
下,无界通配符是一种有用的方法。
1,如果你正在编写一个可以使用 Object 类中提供的功能实现的方法。
2,当代码使用通用类中不依赖于类型参数的方法时。例如, List.size 或 List.clear 。事实上,
Class 之所以这么经常使用,是因为 Class 中的大部分方法都不依赖于T。
以下两个方法:
public static void printList1(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
调用两种方法的区别
List<Integer> li = Arrays.asList(1, 2, 3);
List<String> ls = Arrays.asList("one", "two", "three");
printList1(li); //编译不通过,不能应用在该方法上
printList(ls);
要注意 List list和 List<?> list 不同。你可以将 Object 或 Object 的任何子类型插入 List<?> list。但是你只能将 null 插入 List list。
3,下限通配符
“上限通配符”部分显示,上限通配符将未知类型限制为特定类型或该类型的子类型,并使用 extends 关
键字表示。以类似的方式,下限通配符将未知类型限制为特定类型或该类型的超类型。
下限通配符使用通配符( ? )表示,后跟 super 关键字,
注意:你可以为通配符指定一个上限,也可以指定一个下限,但不能同时指定两者
假设你要编写一个将 Integer 对象放入列表的方法。为了最大程度地提高灵活性,你希望该方法可用
于 List , List 和 List (可以容纳 Integer 值的任何内容)。要编写对 Integer 的列表和 Integer 的超类型(如 Integer 、 Number 和 Object )的方法,你可以指定 List 。术语 List 比 List 的限制性更强,因为前者只匹配 Integer 类型的列表,而后者则匹配作为 Integer 的超类型的任何类型的列表。
以下代码将数字1到10添加到列表的末尾:
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
下限通配符只能存不能取