Java泛型

一、泛型简介

泛型是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);
		
	}
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值