重点理解:泛型类可以看作普通类的工厂;泛型方法;类型变量的限定;类型擦除;
有关泛型转换的事实:虚拟机中没有泛型,只有普通的类和方法; 所有的类型参数都用它们的限定类型转换; 桥方法被合成来保持多态; 为保持类型安全性,必要时插入强制类型转换。
泛型程序设计需要重点理解的内容:
声明:代码借用的“莫等闲”的,博客地址:https://blog.csdn.net/sunxianghuang/article/details/51982979。尊重原创和著作权。
泛型类型:用于通用型程序的设计。
//泛型类型
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
原始类型:Object型是所有类型的超类,本是同根生的好处,其它的所有类型都可以向上转型为原始类。
//原始类型
class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
因为在Pair<T>中,T是一个无限定的类型变量,所以用Object替换。如果是Pair<T extends Number>,擦除后,类型变量用Number类型替换。
public class ReflectInGeneric {
public static void main(String[] args) throws IllegalArgumentException,
SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
ArrayList<Integer> array=new ArrayList<Integer>();
array.add(1);//这样调用add方法只能存储整形,因为泛型类型的实例为Integer
array.getClass().getMethod("add", Object.class).invoke(array, "asd");
for (int i=0;i<array.size();i++) {
System.out.println(array.get(i));
}
}
}
利用反射促使array向上转型为原始类,这种情况下就可以接受String类了。
程序中定义了一个ArrayList<Integer>泛型类型,如果直接调用add方法,那么只能存储整形的数据。不过当我们利用反射调用add方法的时候,却可以存储字符串。这说明ArrayList<Integer>泛型信息在编译之后被擦除了,只保留了原始类型,类型变量(T)被替换为Object,在运行时,我们可以行其中插入任意类型的对象。
注意:不推荐以这种方式操作泛型类型,因为这违背了泛型的初衷(减少强制类型转换以及确保类型安全)。当我们从集合中获取元素时,默认会将对象强制转换成泛型参数指定的类型(这里是Integer),如果放入了非法的对象这个强制转换过程就会出现异常。
如果不指定泛型类型,编译器将根据输入的参数自动识别。对于输入参数类型不一致的情况,采用 几种 参数 类型的共同父类的最小级,直到Object。public class Test {
public static void main(String[] args) {
/**不指定泛型的时候*/
int i=Test.add(1, 2); //这两个参数都是Integer,所以T替换为Integer类型
Number f=Test.add(1, 1.2);//这两个参数一个是Integer,另一个是Float,所以取同一父类的最小级,为Number
Object o=Test.add(1, "asd");//这两个参数一个是Integer,另一个是String,所以取同一父类的最小级,为Object
/**指定泛型的时候*/
int a=Test.<Integer>add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类
int b=Test.<Integer>add(1, 2.2);//编译错误,指定了Integer,不能为Float
Number c=Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float
}
//这是一个简单的泛型方法
public static <T> T add(T x,T y){
return y;
}
}
重点理解:java编译器是通过先检查代码中泛型的类型,然后再进行类型擦除,再进行编译的。
泛型相关面试题(转载)
1. Java中的泛型是什么 ? 使用泛型的好处是什么?
泛型是一种参数化类型的机制。它可以使得代码适用于各种类型,从而编写更加通用的代码,例如集合框架。
泛型是一种编译时类型确认机制。它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。
2、Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型的正常工作是依赖编译器在编译源码的时候,先进行类型检查,然后进行类型擦除并且在类型参数出现的地方插入强制转换的相关指令实现的。
编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List<String>在运行时仅用一个List类型来表示。为什么要进行擦除呢?这是为了避免类型膨胀。
3. 什么是泛型中的限定通配符和非限定通配符 ?
限定通配符对类型进行了限制。有两种限定通配符,一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界,另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面<?>表示了非限定通配符,因为<?>可以用任意类型来替代。
4. List<? extends T>和List <? super T>之间有什么区别 ?
这和上一个面试题有联系,有时面试官会用这个问题来评估你对泛型的理解,而不是直接问你什么是限定通配符和非限定通配符。这两个List的声明都是限定通配符的例子,List<? extends T>可以接受任何继承自T的类型的List,而List<? super T>可以接受任何T的父类构成的List。例如List<? extends Number>可以接受List<Integer>或List<Float>。在本段出现的连接中可以找到更多信息。
5. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用T, E or K,V等被广泛认可的类型占位符。泛型方法的例子请参阅Java集合类框架。
{}
面向对象程序设计:https://www.cnblogs.com/1qaz/p/6492465.html
{}