一、泛型简介
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
让我们来看下面一个列子,使用泛型前
public class GenericBean {
private Object data;
public void setData(Object obj) {
this.data = obj;
}
public Object getData() {
return this.data;
}
}
public class GenericTest {
public static void main(String[] args) {
GenericBean genericBean = new GenericBean();
genericBean.setData("123");
//必须明确的知道实际的参数类型
String data=(String) genericBean.getData();
//转换错误,编译正常,但是运行期间抛出类型转换异常
Integer dataInteger=(Integer) genericBean.getData();
}
}
用Object来保存,如果不知道Object具体是哪一种类型,则容易出现类型转换错误。
使用泛型来解决这个安全问题
public class GenericBean<T> {
private T data;
public void setData(T obj) {
this.data = obj;
}
public T getData() {
return this.data;
}
}
public class GenericTest {
public static void main(String[] args) {
//String类型
GenericBean<String> stringBean = new GenericBean<String>();
stringBean.setData("123");
// stringBean.setData(123);//编译错误 只能传入String类型
String strData=stringBean.getData();
//Integer类型
GenericBean<Integer> integerBean = new GenericBean<Integer>();
integerBean.setData(123);
Integer mIntegerData=integerBean.getData();
}
}
泛型作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
让我们来看看编译后的class
//GenericBean.clss
public class GenericBean<T>
{
private T data;
public void setData(T obj)
{
this.data = obj;
}
public T getData() {
return this.data;
}
}
//GenericTest.class
public class GenericTest
{
public static void main(String[] args)
{
GenericBean stringBean = new GenericBean();
stringBean.setData("123");
String strData = (String)stringBean.getData(); //传入String,自动帮我们转换为String类型
GenericBean integerBean = new GenericBean();
integerBean.setData(Integer.valueOf(123));
Integer mIntegerData = (Integer)integerBean.getData();//传入Integer,自动帮我们转换为Integer类型
GenericBean dateBean = new GenericBean();
dateBean.setData(new Date());
Date data = (Date)dateBean.getData(); //传入Date,就自动帮我们转为Date类型
}
}
我们来验证一下泛型只是给编译器看的。
public static void main(String[] args){
/*
* 验证泛型只是给编译器看的。
*/
//指定只能传入Integer类型,JDK1.7以后,List只需要指定前面<>中的泛型
ArrayList<Integer> list=new ArrayList<>();
//通过反射添加数据,从而绕过编译器,也可以添加其他类型。从而证明泛型存在于编译期间
Class classObj=list.getClass();
classObj.getMethod("add", Object.class).invoke(list, "abc");//通过反射传入字符串
Object value=list.get(0); //abc
}
二、类型通配符
让我们看下面一个案例
public class GenericTest {
private GenericBean<Object> data;
public static void main(String[] args) {
GenericTest genericTest = new GenericTest();
// String类型
GenericBean<String> stringBean = new GenericBean<String>();
genericTest.setData(stringBean);// 编译错误
// Integer类型
GenericBean<Integer> integerBean = new GenericBean<Integer>();
genericTest.setData(stringBean);// 编译错误
}
public void setData(GenericBean<Object> data) {
this.data = data;
}
public GenericBean<Object> getData() {
return data;
}
}
编译错误,提示:
The method setData(GenericBean<Object>) in the type GenericTest is not applicable for the arguments (GenericBean<String>)
假设GenericBean<Object>在逻辑上可以视为GenericBean<String>的父类,那么问题就出来了,通过getData()方法取出数据时到底是什么类型呢?String? Float? 还是Number?因此,在逻辑上GenericBean<Object>不能视为GenericBean<String>的父类。
我们需要一个在逻辑上可以用来表示同时是GenericBean<Integer>和GenericBean<Number>的父类的一个引用类型,由此,类型通配符应运而生。
类型通配符一般是使用 ? 代替具体的类型实参。注意了,此处是类型实参,而不是类型形参!且GenericBean<?>在逻辑上是GenericBean<Integer>、GenericBean<Number>...等所有GenericBean<具体类型实参>的父类。由此,我们依然可以定义泛型方法,来完成此类需求。
public class GenericTest {
public static void main(String[] args) {
GenericBean<String> name = new GenericBean<String>();
GenericBean<Integer> age = new GenericBean<Integer>();
GenericBean<Number> number = new GenericBean<Number>();
getData(name);
getData(age);
getData(number);
}
public static void getData(GenericBean<?> data) {
System.out.println("data :" + data.getData());
}
}
类型通配符上限和类型通配符下限
在上面的例子中,如果需要定义一个功能类似于getData()的方法,但对类型实参又有进一步的限制:只能是Number类及其子类。此时,需要用到类型通配符上限。
public class GenericTest {
public static void main(String[] args) {
GenericBean<String> name = new GenericBean<String>();
GenericBean<Integer> age = new GenericBean<Integer>();
GenericBean<Number> number = new GenericBean<Number>();
getData(name);
getData(age);
getData(number);
//getUpperNumberData(name); // 1
getUpperNumberData(age); // 2
getUpperNumberData(number); // 3
}
public static void getData(GenericBean<?> data) {
System.out.println("data :" + data.getData());
}
public static void getUpperNumberData(GenericBean<? extends Number> data){
System.out.println("data :" + data.getData());
}
}
此时,显然,在代码//1处调用将出现错误提示,而//2 //3处调用正常
类型通配符上限通过形如GenericBean<? extends Number>形式定义,相对应的,类型通配符下限为GenericBean<? super Number>形式,其含义表示只能接受Number及其父类.
三、泛型方法
在方法返回值前定义泛型参数,可定义多个,泛型参数可用于方法参数,返回值,方法内部。
public class GenericBean{
// 1个泛型参数
public <E> E genericMethod1(E param) {
return param;
}
// 多个泛型参数
public <E, F> void genericMethod2(E param1, F param2) {
GenericBean genericBean = new GenericBean<>();
genericBean.genericMethod2(param1, param2);//泛型可传递
}
public <E> E genericMethod3(Object o) {
E e = (E) o;// 方法内部
return e;// 返回值
}
}
四、泛型接口
public interface GenericInterface <E> {
void set(E data);
E get();
<F extends Number> F add(F param);
}
//泛型接口实现类也必须指定泛型参数
public class GenericImpl<E> implements GenericInterface<E>{
private E data;
@Override
public void set(E data) {
this.data=data;
}
@Override
public E get() {
return data;
}
@Override
public <F extends Number> F add(F param) {
return param;
}
}
五、泛型类
public class GenericBean2<K, V> {
private K key;
private V value;
public GenericBean2(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) {
this.key = key;
}
public K getKey() {
return key;
}
public void setValue(V value) {
this.value = value;
}
public V getValue() {
return value;
}
public void copy(K key,V value){
setKey(key);
setValue(value);
}
public void copyTo(GenericBean2<K, V> genericBean){
genericBean.copy(this.key,this.value);//泛型参数可传递
}
}
//子类必须继承父类的泛型参数
public class SubGenericBean<K, V> extends GenericBean2<K, V>{
public SubGenericBean(K key, V value) {
super(key, value);
}
}
//子类添加自己的泛型参数
public class SubGenericBean<K, V,F> extends GenericBean2<K, V>{
F data;
public SubGenericBean(K key, V value) {
super(key, value);
}
}