(1)本质
(2)参数化类型
(1)保证类型一致性
ClassCastException(2)使用泛型能写出更加灵活通用的代码
(3)泛型能够省去类型强制转换
(1)泛型擦除介绍
(2)泛型擦除的过程
编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段
可以看到泛型就是在使用泛型代码的时候,将类型信息传递给具体的泛型代码。而经过编译后,生成的.class文件和原始的代码一模一样,就好像传递过来的类型信息又被擦除了一样
测试:查看类的信息以及通过反射使用其他参数类型
public class GenericTest {
public static void main(String[] args) {
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class strListClass = stringArrayList.getClass();
Class intListClass = integerArrayList.getClass();
System.out.println(strListClass);//class java.util.ArrayList
System.out.println(intListClass);//class java.util.ArrayList
System.out.println(strListClass == intListClass);//true
Method add = strListClass.getMethod("add",Object.class);
add.invoke(stringArrayList, Integer.valueOf(100));//[100]
}
}
(1)类型参数
(2)extend关键字
泛型重载了extend关键字,可以通过extend关键字声明类或者方法中的参数类型是某一类或其子类类型或者是某一接口的实现类
例:
public class GenericTest {
public <T extends Comparable<T>> int compare(T o1,T o2){
return o1.compareTo(o2);
}
}
T extend Comparable<T>表明T是实现了Comparable接口的类型List<? extends List> list = new ArrayList<>();
表明list中元素为List接口类型,可以使用List l = list.get(1);但是只能存入List类型,其他类型都会报错
一般在容器中只需要使用一般的泛型声明方式,即<T>就可以表明存储的类型为T或T的子类,比如:
List<CharSequence> list5 = new LinkedList<>();
list5.add("123");
可以使用&连接extends后面的类,表示后面类的公共子类(当有表示实现的接口时需要将接口全部一到后面,例如<T extends String & Queue & List >,如果是这样<T extends Queue & List & String>会报错)
public<T extends Queue & List> void print(T list){
System.out.println(list);
}
public static void main(String[] args) {
GenericTest g1 = new GenericTest();
LinkedList<String> list = new LinkedList<>();//LinkedList实现了List与Queue接口
g1.print(list);//正常
List<String> list2 = new ArrayList<>();
g1.print(list2);//错误
}
(1)引入泛型通配符的意义
请看以下问题:
public class GenericTest {
public <T extends CharSequence> void printValue(List<CharSequence> list) {
System.out.println(list);
}
public <T extends CharSequence> void printValue2(List<? extends CharSequence > list) {
System.out.println(list);
}
public static void main(String[] args) {
GenericTest g1 = new GenericTest();
List<String> list = new ArrayList<>();
g1.printValue(list);//编译器报错
g1.printValue2(list);//正常
}
}
(2) 通配符的使用
?表示可以持有任何类型,但是与泛型类型如T,V,E等,?不能用于定义类和泛型方法
public void print(List<?> list){
System.out.println(list);
}
public static void main(String[] args) {
GenericTest g1 = new GenericTest();
List<String> list = new ArrayList<>();
g1.print(list);//正常
List<Integer> list2 = new ArrayList<>();
g1.print(list2);//正常
}
上界通配符 < ? extends E>表示参数化的类型可能是所指定的类型,或者是此类型的子类或实现类
public void print(List<? extends CharSequence> list){
System.out.println(list);
}
public static void main(String[] args) {
GenericTest g1 = new GenericTest();
List<String> list = new ArrayList<>();
g1.print(list);//正常
List<StringBuffer> list2 = new ArrayList<>();
g1.print(list2);//正常
}
下界通配符 < ? super E>表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
public void print(List<? super String> list){
System.out.println(list);
}
public static void main(String[] args) {
GenericTest g1 = new GenericTest();
List<String> list = new ArrayList<>();
g1.print(list);//正常
List<CharSequence> list2 = new ArrayList<>();
g1.print(list2);//正常
}
(3)使用场景
上界通配符主要用于读数据,下界通配符主要用于写数据。
public void printValue2(List<? extends CharSequence > list) {
System.out.println(list);
list.add("String");//报错
CharSequence s = list.get(1);//正常
}
list存储的元素为CharSequence实现类,但是本身CharSequence的实现类不止String一种,只有在方法外部我才知道list具体存储元素是什么,所以编译器就不允许存入确定的类型;list存储元素为CharSequence或其子类,因此可以用CharSequence或其父类引用指向其中的元素(多态) public void printValue2(List<? super CharSequence > list) {
System.out.println(list);
list.add("123");//正常
CharSequence s = list.get(1);//报错
}
list中存储元素都为CharSequence实现类的父类,所以将子类插入集合,即作为其本身或父类来使用(多态)是允许的(1)泛型类介绍
泛型类型用于类的定义中,该类可以使用所指定的泛型类型作为属性,该类被称为泛型类
例:链表的节点类:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
(2)泛型类作用
(3)泛型类使用约束
如果该类为某一泛型类的子类时,如果extends关键字父类使用泛型,则在子类的声明中也至少应该加入对应的泛型声明
子类的泛型声明范围必须不超出父类的泛型范围
class S<E extends CharSequence> extends F<E > {
}//正确
class S<E> extends F<E extends CharSequence> {
}//编译器报错
泛型的类型参数只能是对象类型(包括自定义类),不能是简单类型
不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错
System.out.println(integerArrayList instanceof List<Integer>);//报错
(1)泛型接口介绍
泛型接口与泛型类的定义及使用基本相同。
public interface List<E> extends Collection<E> {
...
}
(2)泛型接口的约束
当未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类,如果类的声明中不加与实现的接口相对应的泛型信息,编译器会报错
例:java.util.LinkedList:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
...
}
传入泛型实参时:
定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator
在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型,
public interface Generictor<T> {
T get();
}
public class GenericTest<T> implements Generictor<T> {
T obj;
public GenericTest(T obj){
this.obj = obj;
}
@Override
public T get() {
return obj;
}
public static void showObj(GenericTest<Number> g){
System.out.println(g.get());
}
}
(1)泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型
例如
public class Displayer<E> {
public<T> void display(T object){
System.out.println(object);
}
}
class Demo02{
public static void main(String[] args) {
new Displayer().display("你好,世界");
}
}
说明:
public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型TT与类中声明的E为两种类型,实际上如果两者声明的泛型表示相同,则会判定为方法中声明的泛型类型(2)静态泛型方法
静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上,即如果静态方法要使用泛型的话,必须将静态方法也必须定义成泛型方法
注意:
如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
即使静态方法要使用泛型类中已经声明过的泛型也不可以
public static <T> void staticMethod(T t){
...
}
(1)泛型数组不可被直接创建
下述代码将会报错
List<String>[] ls = new ArrayList<String>[10];
原因解释:
//创建元素为存储Integer类型的数组表的泛型数组
List<Integer>[] list = new ArrayList[10];
//使用父类Object数组指向list
Object[] objects = list;
//创建存储元素为String类型的数组表
List<String> stringList = new ArrayList<>();
stringList.add("String");
//Object类型可以指向任何子类,但是该stringList存储元素为String类型
objects[0] = stringList;
//但是此时我们可能依然以为objects[]数组中第一个存储着List<Integer>类型的表,当强制转换时会出错
Integer s = ((List<Integer>)objects[0]).get(0);//java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
System.out.println(s);
(2)使用通配符创建泛型数组
List<?>[] lists = new LinkedList<?>[10];//实际使用上与List<Object>没有太大区别
List<? extends Comparable>[] lists = (List<? extends Comparable>[]) new LinkedList<?>[10];
数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Correct.
Integer i = (Integer) lsa[1].get(0); // OK
(3)在引用中声明泛型,而不在new中声明泛型
该方法不能保证类型转换时的正确性,可能会出现ClassCastException,如下:
List<Integer>[] list = new ArrayList[10];
原文:https://www.cnblogs.com/nishoushun/p/12640164.html