常见问题:
-
集合中添加元素后,元素会被强制转换为Object类型的元素,再次取出的时候就会失去原本的特性和状态
-
如果取出来元素进行再一次的强制转换,容易引发
ClassCastException
-
添加一种对于元素类型的描述参数,这种类型参数称为泛型,泛型会在编译时检查异常,泛型相当于是一种类型标记
1.泛型的概念和详细用法
1.1概念
-
泛型:就是允许在定义类、接口时通过一个标识表示类中某个属性的类型、某个方法的返回值或方法的参数类型
-
是一种把确定类型的工作推迟到创建对象或者调用方法的时候的参数化类型(把类型当做参数一样传递)
-
这个类型参数将在使用时传入实际的类型参数,如果不传递实际参数默认为Object
-
泛型典型案例就是集合,集合在存储数据时总是将数据默认转换为Object,而取数时也是Object导致实际数据类型丢失,使用泛型就能避免类型的丢失
1.2 自定义泛型
//自定义泛型类
class User<T> implements Comparable<User<String>>{
//自定义泛型属性
T id;
//自定义构造器
public User(T id){
this.id=id;
}
//自定义泛型方法
public <E> E show(){
E name=null;
return name;
}
@Override
public int compareTo(User<String> o) {
return id.equals(o)?1:-1;
}
//自定义方法参数
public void play(T id){
System.out.println(this.id=id);
}
}
//自定义泛型接口
interface Pa<T>{
public T text();
}
1.3泛型定义时的注意事项
-
泛型的参数类型必须是类类型,不能是基本数据类型
-
如果一个类定义了泛型,在使用时(实例化)没有指定泛型的类型,则默认为Object类
-
泛型可以用于定于方法类型、方法参数类型、属性类型
-
如果一个类定义了泛型,在实例化时建议使用,否则很容易引发
ClassCastException
-
泛型的参数class Test{},泛型的定义参数可以是任意字母开头即可–以下都是允许的,但是:建议采用以下英语单词首字母,例如:
-
E
-Element(在集合中使用,因为集合中存放的是元素,首字母) -
T
-Type(Java类) -
K
-Key(键) -
V
-Value(值) -
N
-Number(数值类型) -
?
-表示不确定的java类型
-
-
S
、U
、V
-第二个参数、第三个参数、第四个参数(Java类)
interface foo<T123456789,A2_a,B3_$,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,U,V,W,X,Y,Z,x,y>{
T123456789 show();
}
泛型的主要优点就是将可能出现的类型转换问题规避,将运行时的异常能够在编译时检测出来
-
一个泛型的声明可能存在多个参数,多个参数之间用
,
隔开 -
泛型类的构造器依旧正常声明,不能使用如下声明方式:public GenericClass(){}//错误
-
相同类声明的不同对象,如果指定的泛型不一致无法相互赋值
-
泛型参数是无法被加载到JVM中的,例如
ArrayList<String> strList=new ArrayList<>()
和ArrayList<Integer> intList=new ArrayList<>()
,只有ArrayList类会被加载到JVM中,String和Integer不会被加载到JVM中 -
泛型擦除:如果定义了泛型,但是在使用时没有指定具体的泛型,将会被默认指定为Object,这就叫做泛型擦除
-
泛型类型不能用static修饰,在static方法中不允许使用泛型变量和泛型方法(static修饰的方法和变量在类加载的时候就已经被加载了),但是泛型的赋值是在类加载以后;但是static不可以修饰泛型变量,可以修饰泛型方法
abstract class Person<T>{ // static T id;//不允许 T id; static void show(){ // System.out.println(id);//不允许 // User<T> u=new User("name");//不允许 User u=new User("name");//这里使用了泛型擦除,默认为了Object System.out.println(u.show());//允许的 } T play(){ return id; } }
-
异常类不能是泛型的
-
泛型数组如何声明:
//E[] elements=new E[]//错误的 E[] elements=(E[])new Object[]//允许的
-
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型
-
子类不保留父类的泛型
//类型擦除 class Person<T1,T2>{ } class Child extends Person{//相当于Person<Object,Object> } //实例化时 Person<Object, Object> objectObjectPerson = new Person<>(); //指定父类的具体类型但是不保留 class Person<T1,T2>{ void show(T1 a,T2 b){ System.out.println(a+"="+b); } } class Child<T3,T4> extends Person<Integer,String>{ //在子类中父类的泛型被指定,如果是实例化父类或多态实例化的时候还是需要指定父类的泛型参数 void play(){ show(15, "20"); } } Person person= new Child(); person.show("12","34");
-
子类保留父类的泛型(泛型子类)
//全部保留 //在使用泛型参数当作实参传递时就必须强转,这样其实是不推荐的--如果必须要使用建议在被调用的方法内使用instanceOf判断一下 class Person<T1,T2>{ void show(T1 a,T2 b){ System.out.println(a+"="+b); } } class Child<T1,T2> extends Person<T1,T2>{ void play(){ show((T1)"java", (T2)"hadoop"); } } //保留后,给子类指定泛型就等于给父类指定泛型 Child<Integer, String> objectObjectChild = new Child<>(); objectObjectChild.show(15, "20"); //部分保留 class Person<T1,T2>{ void show(T1 a,T2 b){ System.out.println(a+"="+b); } } class Child<T1,B2> extends Person<T1,String>{//保留一个,未保留部分必须指定泛型,否则报错--报错原因是因为无法默认为Object类型 void play(T1 a,B2 b){ show((T1)"java", "hadoop"); } } Child<Integer, Integer> objectObjectChild = new Child<>(); objectObjectChild.show(15, "20"); objectObjectChild.play(15,16);
-
泛型的菱形语法:
JDK7以前:
List list=new ArrayList()
JDK7以后:
List list=new ArrayList<>()
2.泛型的类型通配符
泛型类型的通配符:‘?’
-
泛型的通配符不能在声明时使用,例如声明为泛型类、泛型方法时,必须指定一个类型参数
-
如果在使用时,如创建泛型类的对象时,使用了?通配符,则不能传递任何类型的元素,只能传null,null是所有类型的成员
Person<?, ?> objectObjectPerson = new Person<>(); objectObjectPerson.show("abc","drg" ); // 编译时错误 此时的参数是:show(capture of ?,capture of ?)
-
在集合中,如果创建集合对象时将泛型指定为?通配符,则不允许添加元素–因为程序无法确定list集合中元素的类型,万一后面你给定了的类型和你添加的元素类型不一致,那就出问题了
-
我们可以理解为被<?>指定为通配符的类型只能读不能写,例如:
List<String> list=new ArrayList<>(); list.add("java"); objectObjectChild.test(list); public void test(List<?> list) {//此时会将String与?进行匹配,?可以匹配任意类型的泛型 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); System.out.println(list.get(i).getClass()); } for (Object o : list) {//自动用Object类接收 System.out.println(o.getClass()); } }
限制泛型通配符:
<?>
:是指允许所有泛型的引用调用
<? extends 对象>
:表示使用时指定的类型必须是继承于该对象,即该对象为父类上限
<? super 对象>
:表示使用时指定的类型必须是该对象的父类,即该对象为子类下限
<? extends 接口>
:表示使用时指定的类型必须是该接口的实现类
3.泛型方法
自定义泛型方法–必须要在泛型E前面加上,否则编译器会优先认为E是一个类,因为class E{}也是被允许的
public <E> E show(){
E name=null;
return name;
}