java泛型小记录

之前对泛型就是停留在会使用而已,今天把泛型的一些重要的例子记录一下以及对深层次的理解一下。
泛型小例子:

package zsc.lian.test;

import java.lang.reflect.InvocationTargetException;
import java.text.Annotation;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CollectionTest {
	public static void main(String[] args) throws IllegalArgumentException,
			SecurityException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {

		/*
		 * 错误一 ,道理很直观,从编译器的角度上来说li原本是可以装任意类型的数据,
		 * 但是在实际内存可以装整型的数据,这样就产生了矛盾。因为在后面取出数据
		 * 时,就只可以取出整型类型的数据,但是我们规定却可以取出任意类型的数据, 所以矛盾,在编译期间就会报错
		 */
		// Mylist<Object> li = new Mylist<Integer>();
		// 纠正Mylist<Object> li = new Mylist();

		/*
		 * 错误二,道理仍然很直观,、从编译器角度来说li可以装整型的数据,但是实际上
		 * 在内存可以存放任意类型的数据,那么当取出数据时,取出的数据就什么类型都可以,
		 * 但是我们规定只能取出Integer类型的数据,所以就矛盾,编译就不通过。
		 */
		// Mylist<Integer> li = new Mylist<Object>();
		// 纠正Mylist<Integer> li = new Mylist();

		/*
		 * 错误三,不能使用这种List<String>[] lsa = new ArrayList<String>[10];
		 * List<String>[] lsa = new ArrayList<String>[10]; // illegal Object[]
		 * oa = lsa; // OK because List<String> is a subtype of Object
		 * List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3));
		 * oa[0] = li; String s = lsa[0].get(0); 最后一行将抛出
		 * ClassCastException,因为这样将把 List<Integer>填入本应是 List<String>的位置。
		 * 因为数组协变会破坏泛型的类型安全,所以不允许实例化泛型类型的数组(除非类型参数是未绑定的通配符,比如 List<?>)
		 */
		// 纠正List<?>[] lsa = new List<?>[3];

		/*
		 * 第一种方式,利用这种骗过编译器
		 */
		Mylist ii = new Mylist<Integer>();
		Mylist<Object> mylist = ii;
		mylist.add("33");
		mylist.PrintVaule();

		/*
		 * 第二种方式,利用反射绕过编译器
		 */
		Mylist<Integer> li1 = new Mylist<Integer>();
		li1.getClass().getMethod("add", Object.class).invoke(li1, "adf");
		li1.PrintVaule();

		printMyList(li1);

		// 通配符扩展
		/*
		 * 限定通配符的上边界
		 */
		Mylist<? extends Number> x = new Mylist<Integer>();// right,因为Integer继承了Number
		// Mylist<? extends Number> x1 = new Mylist<String>(); wrong

		/*
		 * 限定通配符的下边界
		 */
		Mylist<? super Integer> x2 = new Mylist<Number>();// right,因为Number是Integer的父类
		// Mylist<? super String> x3 = new Mylist<Number>();wrong

		try {
			Class<?> c = Class.forName("java.lang.String");
			System.out.println(c);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		/*
		 * 将父类的class对象强制转成子类对象。不能是其他对象,必须是子类的
		 */
		Class<? extends Number> cs = Integer.class.asSubclass(Number.class);
		System.out.println(cs.getCanonicalName());
		
		A a = new A();
		System.out.println(a.getClass().getName());
		Class<? extends A> cs1 = B.class.asSubclass(A.class);
		System.out.println(cs1.getName());
		
		//调用change方法
		change(new String[]{"asdfa","sdfas","sdfe"},1,2);

	}

	/*
	 * 报错四,原因见上面错误一二 public void printMyList(Mylist<Object> li1){
	 * 
	 * }
	 */

	/*
	 * 报错五,因为不知传入过来的是什么类型,万一传入一个Mylist<Integer> 的呢?那么我们就无法li1.add("asd") public
	 * static void printMyList(Mylist<?> li1){ li1.add("adfd"); }
	 */
	//解决方法
	public static <T> void printMyList1(Mylist<T> list,T obj){
		list.add(obj);
	}
	
	/*
	 * ?号是通配符,可以指向任意类型
	 */
	public static void printMyList(Mylist<?> li1) {
		li1 = new Mylist<Date>();
	}
	
	/*
	 * 定义类似C++模版,对任意类型的数组交换位置
	 */
	public static <T> void change(T[] array,int i,int j){
		T k = array[i];
		array[i] = array[j];
		array[j] = k;
	}

}

class A{
	
}

class B extends A{
	
}

class C extends A{
	
	/*
	 * 只能返回A的子类,返回其他类型报错
	 */
	public <Y extends A> Y getMyA(Class<Y> A){
		return (Y) new B();
		
	}
	
}


class Mylist<I> {
	Object o;
	Object o1;

	public Mylist() {

	}

	public Mylist(I i) {
		o = i;
		System.out.println(o);
	}

	public void add(I e) {
		o1 = e;
	}

	public void PrintVaule() {
		System.out.println(o1.getClass().getName());
	}
}



泛型例子二:

package zsc.lian.test;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;

//class GenericDao {
//	public <T> void save(T t){
//		
//	}
//	
//	public <T> T findId(int id){
//		return null;
//	}
//}
//解决方法
class GenericDao<T> {

	public void save(T t){
		
	}
	
	public T findId(int id){
		return null;
	}
	
	public static void applyVector(Vector<Date> v){
		
	}
	
	//错误,静态方法不能这样使用泛型
//	public static void update(T obj){
//		
//	}
}

class Person{
	
}

public class Test{
	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		/*
		 * 插入和返回的数据类型不一致,即添入是什么
		 * 类型的数据,取出应该就是什么类型的数据
		 */
		GenericDao<Person> Gd = new GenericDao<Person>();
		Gd.save(new Person());
		Person s = Gd.findId(1);
		for (int i = 0; i < 10; i++) ; //为了打印StackMapTable信息
		
		Method method = Gd.getClass().getMethod("applyVector",Vector.class);
		Type[] type = method.getGenericParameterTypes();
		ParameterizedType pt = (ParameterizedType) type[0];
		System.out.println(pt.getActualTypeArguments()[0]);
	}
}


之前对一直对这个例子很迷惑,为什么会迷惑,因为,总所周知,泛型只是做给编译器看的而已,也就是说由编译器编译后泛型信息会被擦除,class文件不会保存泛型的信息。那么上面那个例子GenericDao类中怎么可以通过反射来确定Vector可以装Date类型的数据呢?难道并且没有擦除,于是用了JAD反编译一下查看到了代码,

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Test.java

package zsc.lian.test;

import java.util.Vector;

class GenericDao
{

    GenericDao()
    {
    }

    public void save(Object obj)
    {
    }

    public Object findId(int id)
    {
        return null;
    }

    public static void applyVector(Vector vector)
    {
    }
}


查看到GenericDao.Class文件中确实没有保存泛型的信息,确实是被擦除了。那原因是处在哪里呢?请教了几个工作多年的朋友,也没有得到很好的回答。后来在网上搜到一篇不错的文章:http://rednaxelafx.iteye.com/blog/586212
里面说道:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
具体可以查看此文章。
确实,在java 5,Class文件中确实加入了泛型的信息。但是只是在声明一侧加入了。
查看Test.Class的数据类型:

 

  StackMapTable: number_of_entries = 2
           frame_type = 254 /* append */
             offset_delta = 33
        locals = [ class zsc/lian/test/GenericDao, class zsc/lian/test/Person, int ]
           frame_type = 2 /* same */
 

 

从上面看泛型信息被擦除了,接着看看GenericDao.Class的信息

  public static void applyVector(java.util.Vector<java.util.Date>);
    flags: ACC_PUBLIC, ACC_STATIC

    Signature: #30                          // (Ljava/util/Vector<Ljava/util/Date;>;)V
    Code:
      stack=0, locals=1, args_size=1
         0: return        
      LineNumberTable:
        line 31: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       1     0     v   Ljava/util/Vector;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       1     0     v   Ljava/util/Vector<Ljava/util/Date;>;
}
 

既然保存了泛型的信息,那么就可以通过反射获取泛型的具体类型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值