一、泛型的意义
泛型的本质是将类型参数化,从而达到代码复用。
即:在不创建新的类型下,通过泛型指定不同类型来控制形参具体类型,简单来讲就是,当我们不知道用什么数据类型接收数据的情况下,可以使用泛型来接收。
代码示例:
未使用泛型情况下:
private static int add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
private static float add(float a, float b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
private static double add(double a, double b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
使用泛型情况下:
private static <T extends Number> double add(T a, T b) {
System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
return a.doubleValue() + b.doubleValue();
}
二、泛型的使用
注:E e:Element元素,T t:Type类型
1.泛型类
格式:
修饰符 class 类名<泛型类型> { . . . }
代码示例:
class GenericClass<E> {
private E name;
public void setName(E name) {
this.name=name;
}
public E getName() {
return this.name;
}
}
public class GenericDemo {
public static void main(String[] args) {
//使用泛型,限定该类的数据类型为String类
GenericClass<String> name1=new GenericClass<String>();
name1.setName("使用泛型");
String str1=name1.getName();
System.out.println(str1);
//不使用泛型,该类的数据类型默认为Object类
GenericClass name2=new GenericClass();
name2.setName("不使用泛型");
Object obj1=name2.getName();
System.out.println(obj1);
}
}
2.泛型方法
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(泛型)){ 方法体 };
3.泛型接口
格式:
interface GenericInterface<I> {
public void method(I i);
}
class Genericing implements GenericInterface<String> {
public void method(String s) {
System.out.println("泛型接口传参为:"+s);
}
}
public class GenericDemo {
public static void main(String[] args) {
Genericing generic=new Genericing();
generic.method("Test");
}
}
4.泛型通配符
<?> 无限制通配符
<? extends E> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
<? super E> super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
// 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:生产者有上限、消费者有下限
1. 如果参数化类型表示一个 T 的生产者,使用 < ? extends T>;
2. 如果它表示一个 T 的消费者,就使用 < ? super T>;
3. 如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
代码示例:
class IteratorArrayList {
//由于不确定接收的ArrayList集合中的元素是什么类型,所以使用泛型通配符 ?
//array1的元素为Integer类型,array2的元素为String类型
public static void printing(ArrayList<?> array1) {
Iterator<?> iterator=array1.iterator();
while(iterator.hasNext()) {
Object obj=iterator.next();//next()方法取出的元素类型是Object,可以接收任意的数据类型
System.out.print(obj + " ");
}
}
}
public class GenericDemo {
public static void main(String[] args) {
//array1集合限定数据类型为Integer
ArrayList<Integer> array1=new ArrayList<Integer>();
array1.add(111);
array1.add(222);
array1.add(333);
array1.add(444);
array1.add(555);
System.out.print("遍历Integer数据类型的array1集合:");
IteratorArrayList.printing(array1);
System.out.println();
System.out.println("====================================");
//array2集合限定数据类型为String
ArrayList<String> array2=new ArrayList<String>();
array2.add("一号");
array2.add("二号");
array2.add("三号");
array2.add("四号");
array2.add("五号");
System.out.print("遍历String数据类型的array2集合:");
IteratorArrayList.printing(array2);
}
}
三、泛型的擦除
1.泛型擦除原则
- 消除类型参数声明,即删除
<>
及其包围的部分; - 根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类);
- 为了保证类型安全,必要时插入强制类型转换代码;
- 自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”。
2.无限制类型擦除
当类定义中的类型参数没有任何限制时,在类型擦除中直接被替换为Object,即形如<T>
和<?>
的类型参数都被替换为Object。
3.有限制类型擦除
当类定义中的类型参数存在限制(上下界)时,在类型擦除中替换为类型参数的上界或者下界,比如形如<T extends Number>
和<? extends Number>
的类型参数被替换为Number
,<? super Number>
被替换为Object。