尚硅谷_Java零基础教程-java入门必备-初学者基从入门到精通全套完整版(宋红康主讲) P565-576
为什么要有泛型
jdk1.5 以后,开始使用泛型。改写了集合框架中的全部接口和类,为它们增加了泛型支持。
为什么要有泛型?
- 解决元素存储的安全性问题,好比商品、药品标签,不会弄错。当没有泛型时,任何类型都可以添加到集合中,类型不安全。
- 解决获取数据元素时,需要类型强制转换的问题。还可能有类型转换错误的问题
在集合中使用泛型
- 集合接口或集合类在jdk5都修改为带泛型的结构。
- 在实例化集合类时,可以指明具体的泛型类型。
- 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用指明的类型
- 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,用包装类代替。
- 如果实例化时,没有指明泛型的类型,默认是Object
自定义泛型结构
泛型类
- 泛型类可能有多个参数,此时应该将多个参数一起放在尖括号内。比如<E1,E2,E3>
- 泛型类的构造器如下:
publicGenericClass(){}
。而这种是错误的:publicGenericClass<E>(){}
- 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
- 泛型不同的引用不能相互赋值。尽管在编译时
ArrayList<String>
和ArrayList<Integer>
是两种类型,但是,在运行时只有一个 ArrayList类被加载到JVM中。 - 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用就前后都用。要么,前后都不用。
- 如果泛型结构是一个接口或抽象类,则不可创建泛型结构的对象。
- jdk1.7,泛型的简化操作:
Order<Integer> order = new Order<>();
- 泛型的指定中不能使用基本数据类型
- 在类/接口 上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参考类型、非静态方法的返回值类型。但在
静态方法中不能使用类的泛型
。 异常类不能是泛型的
- 不能使用new E[]。但是可以E[] elements = (E) new Object[capacity]; 参考:ArrayList源码中声明Object[] elementData,而非泛型参数类型数组。
- 父类有泛型,子类可以选择保留泛型,也可以选择指定泛型类型。
子类不保留父类泛型,可以选择没有类型,即擦除;
也可以指定具体类型。子类还可以部分保留或全部保留。
子类还可以增加自己的泛型。
class Father<T1,T2>{ }
/* 不保留父类泛型 */
//擦除
class Son1 extends Father{ }
//具体类型
class Son2 extends Father<Integer,String>{ }
/* 保留父类泛型 */
//部分保留
class Son3<T1> extends Father<T1,String>{ }
//全部保留
class Son4<T1,T2> extends Father<T1,T2>{ }
泛型方法
泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有关系。
也就是说,泛型方法所属的类可以是泛型类,也可以不是。
泛型方法可以声明为静态,因为泛型参数是在调用方法时确定的。
public class GenericMethod {
//泛型方法
public <T> List<T> copyFromArrayToList(T[] arr){
List<T> list = Arrays.asList(arr);
for (T t:list) {
System.out.println(t);
}
return list;
}
@Test
public void test(){
GenericMethod method = new GenericMethod();
//泛型方法在调用时,指明泛型参数的类型
method.copyFromArrayToList(new Integer[]{1,2,3});// 1\n2\n3
method.copyFromArrayToList(new String[]{"1","2","3"});// 1\n2\n3
}
}
使用情景
DAO:数据访问对象
定义一个DAO的通用接口,或通用类,使用泛型。class DAO<T>{}
在对应数据库database1的DAO,可以写class DataBase1DAO extends DAO<DataBase1>{}
泛型在继承方面的体现
- 假设类A是类B的父类
G<A>
不是G<B>
,它们是并列关系A<G>
是B<G>
的父类
通配符的使用 G<?>
G<A>
不是G<B>
共同的父类是G<?>
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
//list1 = list;//不行
//list2 = list;//不行
//list.add(1); //不行
list.add(null);
但对于List<?>,里面就不能添加数据了(除了null)。
允许读数据,类型为Object。
public void print(List<?> list){
Iterator<?> iterator= list.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
}
有限制条件的通配符
G<? extends A>
:可以作为G(A)和G(B)的父类。G<? super A>
: 可以作为G(A)和G(B)的子类。
public void test2(){
List<? extends Person> list1 = null;
List<? super Person> list2 = null;
List<Student> list3 = null;
List<Person> list4 = null;
List<Object> list5 = null;
list1=list3;
list1=list4;
list1=list5;//编译错误
list2=list3;//编译错误
list2=list4;
list2=list5;
//读取数据
list1 = list4;
Person person = list1.get(0);
list2 = list4;
Object object = list2.get(0);
// Person person = list2.get(0);//编译错误
//写入数据
// list1.add(new Object());//编译错误
list2.add(new Person());
list2.add(new Student());
}
class Person{ }
class Student extends Person{}