一、为什么要有泛型(Generic)?
二、使用泛型
三、泛型的几个重要应用
四、通配符的使用
一、为什么要有泛型(Generic)?
1. 解决元素存储的安全性问题
2. 解决获取数据元素时,需要类型强转的问题
泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。
二、使用泛型
Ⅰ、泛型的声明:
interface List<T> 和 class GenTest<K,V> 其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。
Ⅱ、泛型的实例化:
一定要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
T只能是类,不能用基本数据类型填充。但可以使用包装类填充
把一个集合中的内容限制为一个特定的数据类型,这就是generics(泛型)背后的核心思想
三、泛型的几个重要应用
Ⅰ、在集合中使用泛型
Ⅱ、自定义泛型类
public class Person<E> {
E e;
public void setE(E e){
this.e = e;
}
public E getE(){
return e;
}
}
Ⅲ、自定义泛型方法
public <T> T getT(T t){
return t;
}
public static <T> T getT(T t){
return t;
}
Ⅳ、自定义泛型接口
public interface MyInterface<T> {
T getT();
}
Ⅰ、在集合中使用泛型
1. 对象实例化时不指定泛型的话,默认为:Object。
2. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如<E1,E2,E3>
3. 泛型类的构造器如下:
public GenericClass(){}。而如下是错误的:
public GenericClass<E>(){}
4.从泛型类派生子类,泛型类型需具体化
5.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
6.静态方法中不能使用类的泛型
7.异常类不能是泛型的
8.加入集合中的对象类型必须与指定的泛型类型一致。
9.泛型不同的引用不能相互赋值。
尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
10. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
11. 不能使用new E[]。但是可以:
E[] elements = (E[])new Object[capacity];
注:对于6、7
public V getV() {
/*
* 编译不通过:异常的类型不能是泛型
try {
} catch (T t) {
// TODO: handle exception
}
*/
return v;
}
/*
* 静态方法中不能使用类的泛型,因为泛型的类型是在创建对象的时候指明的
public static K getT(){
return k;
}
*/
注意区分:
静态方法中:类的泛型与泛型方法:
说明:在静态方法中使用类的泛型会泛型编译异常,因为创建对象调用方法时,不能够确定泛型的类。而使用静态方法,则可以确定类型在创建对象调用的时候
class Student<T>{
/* 编译正确
* 静态方法中泛型方法
* 注意:泛型方法是可以使用static来修饰的,因为调用方法的时候我们就知道了泛型的类型
*/
public static <E> E getE2(E e){
return e;
}
/* 编译错误
* 静态中方法中类的泛型
* 注意: 因为创建对象调用该方法时,不能够确定泛型的类型
*/
public static T getT3(){
return t;
}
}
注意区分:
类的泛型定义的一般方法与泛型方法:
说明:自定义的泛型方法必须要在方法名前加上<>并在里面标识泛型
class Student<T>{
T t;
public T getT() {//使用T类型定义的一般方法
return t;
}
public <E> E getE(E e){//自定义泛型方法
return e;
}
}
Ⅱ、自定义泛型类
class Person<T>{
//使用T类型定义变量
private T info;
//使用T类型定义一般方法
public T getInfo(){
return info;
}
public void setInfo(T info){
this.info = info;
}
//使用T类型定义构造器
public Person(){}
public Person(T info){
this.info = info;
}
}
如何给父类指明泛型类型?
第一种方式:子类在继承父类的时候,可以直接声明父类的泛型类型。
public class Student extends Person<String>
第二种方式:子类在继承父类的时候,可以通过子类对象来指明父类的泛型类型
public class Student<T> extends Person<T>
Student<String> student = new Student<String>();
Ⅲ、自定义泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式:[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
Ⅳ、自定义泛型接口
如果Men是Person的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<Man>并不是G<Person>的子类型!比如:String是Object的子类,但是List<String >并不是List<Object>的子类。
public void testGenericAndSubClass() {
Person[] persons = null;
Man[] mans = null;
// 而 Person[] 是 Man[] 的父类.
//在数组上
persons = mans;
Person p = mans[0];
// 在泛型的集合上
List<Person> personList = null;
List<Man> manList = null;
// personList = manList;(报错)
}
四、通配符的使用
1.使用类型通配符:?
比如:List<?> ,Map<?,?>
List<?>是List<String>、List<Object>等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
唯一的例外是null,它是所有类型的成员。
通配符的使用说明:
① 可以理解成通配符是所有泛型的父类
②使用通配符的集合只能存放null值
② 使用通配符的集合可以遍历所有的数据
通配符的限制说明:
<? extends Number> : 泛型只能使用Number和Number的子类
<? extends 接口> : 泛型只能使用接口的实现类
<? super Number> :泛型只能使用Number和Number的父类
。。。