泛型
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。泛型的使用包括泛型类,泛型接口与泛型方法。
2.泛型存在的意义
2.1 既然泛型可以将我们给类,方法或接口中定义的类型转化为变量一样的存在,也就是说泛型可以帮助我们减少代码的重复率(适用于多种数据类型执行相同的代码)。
public static void main(String[] args) {
//现在有个需求是要打印出传入的数组中的所有元素。
//1. 如果没有泛型,那么我们会分别重载不同类型的打印数组的方法,
// 但是有了泛型我们就可以只写一个泛型类接收所有类型的数组。
// 所有基础数据类型都要转换为相应的包装类才能被接收。
Boolean[] booleans = {true, true, false, true};
Integer[] ints = {10, 11, 12, 13};
Double[] doubles = {20.1, 20.2, 20.3};
Character[] chars = {'a', 'b', 'c', 'd'};
String[] strings = {"张三", "李四", "王二", "麻子"};
printArray(booleans);
printArray(ints);
printArray(doubles);
printArray(chars);
printArray(strings);
}
//打印不同类型的数组的泛型方法。
private static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + ",");
}
System.out.println();
}
//运行结果
//true,true,false,true,
//10,11,12,13,
//20.1,20.2,20.3,
//a,b,c,d,
//张三,李四,王二,麻子,
2.2 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
3.泛型的使用
3.1 泛型类
public class Demo9 <T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static void main(String[] args) {
Demo9<String> stringDemo = new Demo9<>();
stringDemo.setData("泛型类");
System.out.println(stringDemo.getData());
Demo9<Integer> intDemo = new Demo9<>();
intDemo.setData(123456789);
System.out.println(intDemo.getData());
}
}
3.2 泛型接口
public interface Demo10 <T> {
T getData();
}
/**
* 实现方法一:指定泛型接口具体类型。
*/
class MethodOne implements Demo10<String> {
String data;
public void setData() {
this.data = data;
}
3.3 泛型方法
public static void main(String[] args) {
//现在有个需求是要打印出传入的数组中的所有元素。
//1. 如果没有泛型,那么我们会分别重载不同类型的打印数组的方法,
// 但是有了泛型我们就可以只写一个泛型类接收所有类型的数组。
// 所有基础数据类型都要转换为相应的包装类才能被接收。
Boolean[] booleans = {true, true, false, true};
Integer[] ints = {10, 11, 12, 13};
Double[] doubles = {20.1, 20.2, 20.3};
Character[] chars = {'a', 'b', 'c', 'd'};
String[] strings = {"张三", "李四", "王二", "麻子"};
printArray(booleans);
printArray(ints);
printArray(doubles);
printArray(chars);
printArray(strings);
}
//打印不同类型的数组的泛型方法。
private static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + ",");
}
System.out.println();
}
4.通配符类型
4.3 泛型的参数类型支持通配符类型。<?>中?代表没有限制的泛型类型。
4.2 泛型的参数类型可以使用extends语句。<? extends Number>中,extends并不代表继承,而是表示?的范围必须是在Number类型的子类型中。这样写告诉编译器这个类一定是Number的子类。所以在写入数据时编译器只知道传入了一个Number或者Number下的子类,但是具体传入了哪个子类不知道。在访问数据时,编译器返回一个Number类型的值,这是编译器可以确定的。
4.3 泛型的参数类型支持super语句。<? super T>中,super表示类型的下界,表示参数化类型是T或者T的父类型。传入数据时编译器知道一定是T或T的父类型,但是具体是什么类型不知道,访问数据时会返回一个Object类型的数据。
5.泛型限制类型
5.1 在使用泛型时,可以指定泛型的限定区域,例如:必须是某某类的子类或某某接口的实现类,格式:<T extends 类或接口1 & 接口2>。比如有一个泛型的限制是<T extends List & Serializable>,那么在使用这个泛型时,< Integer >中Integer没有实现List & Serializable这两个接口就是不符合限制的,而< ArrayList >就是符合限制的。
6.泛型使用的一些注意事项
1、泛型参数列表可以有多个泛型,中间用逗号隔开。例如< T,V,E... >,这些参数都可以进行类型的限制。 2、静态变量或方法不能引用泛型类型变量,但是静态泛型方法是可以的。 3、基本类型无法作为泛型类型。 4、无法使用instanceof关键字或==判断泛型类的类型。 5、泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的。 6、泛型数组可以声明但无法实例化。 7、泛型类不能继承Exception或者Throwable。 8、不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出。 9、对于泛型参数是继承关系的泛型类之间是没有继承关系的。 10、泛型类可以继承其它泛型类,例如: public class ArrayList< E > extends AbstractList< E >。 11、泛型类的继承关系在使用中同样会受到泛型类型的影响。
7.泛型的实现
7.1 在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
7.2 Java泛型是Java1.5之后才引入的,为了向下兼容。Java采用了C++完全不同的实现思想。Java中的泛型更多的看起来像是编译期用的。Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object。
GenericClass<String> stringGenericClass=new GenericClass<>();
GenericClass<Integer> integerGenericClass=new GenericClass<>();
C++中GenericClass< String >和GenericClass< Integer >是两个不同的类型,Java进行了类型擦除之后统一改为GenericClass< Object >
浙公网安备 33010602011771号