java的泛型在编译期有效,但是在运行期所有的类型参数都会被删除。
class example{ public void arrayMethod(String[] strArray){} public void arraymethod(Integer[] intArray){} public void listMethod(List<String> strList){} public void listMethod(List<Integer> intList){} }这个例子编译会出错:Method listMethod(List<String>) has the same erasure listMethod(List<E>) as another method in type example
JAVA泛型在编译期,所有泛型类型都会转换,编译后的字节码是没有任何泛型信息的,也就是说:一个泛型类和一个普通类在编译后指向同一个字节码。这样做的原因:
1:泛型只存在编译期,而不在运行期,避免了JVM的大换血
2:版本兼容,在1.5以上的版本中,List这样的 原生类型也可以正常编译通过,只是会出现警告而已。
因为泛型是类型擦除的,所以:
1:泛型的class对象是相同的,每个类都有class属性,泛型化不会改变其返回值
class example2 { public void test() { List<String> list1 = new ArrayList<String>(); List<Integer> list2 = new ArrayList<Integer>(); System.out.println(list1.getClass() == list2.getClass()); } }2:泛型数组初始化时不能声明泛型类型
// List<String>[] strList; List<String>[] strList =new List<String>[];以上代码编译通不过,因为类型擦除。
3:instanceof不允许存在泛型参数
1:泛型结构只参与”读“的操作,则限定上限(extends关键字)
public static <E> void read(List<? extends E> list){ for (E e : list) { //业务操作 } }
可以推断出List中取出的是E类型的,具体类型在运行时才能确定,但是一定是一个确定的类型。
2:泛型结构只参与”写“的操作,则限定下限(super 关键字)
public static void write(List<? super Number> list){ list.add(23); list.add(12.34); }不用管是23这种Integer还是12.34这种Double类型,都可以加入到list中 ,因为他们都是Number类型的。
JDK中的一个例子Collections.copy方法来说明上限跟下限,它实现了把源列表中的所有元素拷贝到目标列表中对应的索引处。
public static <T> void copy(List<? super T> dest, List<? extends T> src) { int srcSize = src.size(); if (srcSize > dest.size()) throw new IndexOutOfBoundsException("Source does not fit in dest"); if (srcSize < COPY_THRESHOLD || (src instanceof RandomAccess && dest instanceof RandomAccess)) { for (int i=0; i<srcSize; i++) dest.set(i, src.get(i)); } else { ListIterator<? super T> di=dest.listIterator(); ListIterator<? extends T> si=src.listIterator(); for (int i=0; i<srcSize; i++) { di.next(); di.set(si.next()); } } }
源列表是用来提供数据的,所以src要用extends,目标列表是用来写入数据的,所以dest需要用super
如果既要读又要写,那么只需要用确定的泛型类型即可,如List<E>.
1:List<T>是一个确定的类型,只不过是在运行期确定而已
2:List<T>可以进行读写操作, add,remove等操作,因为固定类型为T,所以不需要转型。List<?>只读类型,因为我不能确定你到底是什么类型 ,这样就不安全了,List<?>取出的是Object类型,需要主动转型
class example3<T>{ private static T s; public static void method(T t){ // do method } }出现编译错误 Cannot make a static reference to the non-static type T
总而言之:
1:虚拟机中没有泛型,只有普通的类和方法。
2:所有的类型参数都用他们的限定类型替换
3:桥方法被合成来保持多态
4:为保持类型安全,必要时插入强制类型转换。
一个泛型的实例
package generic; import java.util.Date; import java.util.GregorianCalendar; public class PairTest3 { public static void main(String[] args) { Manager ceo = new Manager("Gus Greedy", 80000, 2003, 12, 15); Manager cfo = new Manager("Sid sneaky", 60000, 2003, 12, 15); Pair<Manager> buddies = new Pair<Manager>(ceo, cfo); printBuddies(buddies); ceo.setBonus(100000); cfo.setBonus(50000); Manager[] managers = { ceo, cfo }; Pair<Employee> result = new Pair<Employee>(); minmax(managers, result); System.out.println(result.getFirst().getName() + result.getSecond().getName()); maxminBonus(managers, result); System.out.println(result.getFirst().getName() + result.getSecond().getName()); } public static void printBuddies(Pair<? extends Employee> p) { Employee first = p.getFirst(); Employee secend = p.getSecond(); System.out.println(first.getName() + secend.getName()); } public static void minmax(Manager[] a, Pair<? super Manager> result) { if (a == null || a.length == 0) return; Manager min = a[0]; Manager max = a[0]; for (int i = 0; i < a.length; i++) { if (min.getBonus() > a[i].getBonus()) min = a[i]; if (max.getBonus() < a[i].getBonus()) max = a[i]; } result.setFirst(min); result.setSecond(max); } public static void maxminBonus(Manager[] a, Pair<? super Manager> result) { minmax(a, result); PairAlg.swapHelper(result); } } class PairAlg { public static boolean hasNulls(Pair<?> p) { return p.getFirst() == null || p.getSecond() == null; } public static void swap(Pair<?> p) { swapHelper(p); } public static <T> void swapHelper(Pair<T> p) { T t = p.getFirst(); p.setFirst(p.getSecond()); p.setSecond(t); } } class Employee { private String name; private double salary; private Date hireDay; public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public void raiseSalary(double byPercent) { double raise = salary * byPercent; salary += raise; } public String getName() { return name; } public double getSalary() { return salary; } public Date getHirDay() { return hireDay; } } class Manager extends Employee { private double bonus; public Manager(String n, double s, int year, int month, int day) { super(n, s, year, month, day); bonus = 0; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } }