编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个 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可以接受 List或 List。在本段出现的连接中可以找到更多信息。
5. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用 T, E or K,V 等被广泛认可的类型占位符。泛型方法的例子请参阅 Java 集合类框架。最简单的情况下,一个泛型方法可能会像这样:
public V put(K key, V value) { return cache.put(key, value); }
List> listOfAnyType; List listOfObject = new ArrayList(); List listOfString = new ArrayList(); List listOfInteger = new ArrayList(); listOfAnyType = listOfString; //legal listOfAnyType = listOfInteger; //legal listOfObjectType = (List) listOfString; //compiler error - in-convertible ty
13、List和原始类型 List 之间的区别.
该题类似于“原始类型和带参数类型之间有什么区别”。带参数类型是类型安全的,而且其类型安全是由编译器保证的,但原始类型 List 却不是类型安全的。你不能把 String 之外的任何其它类型的 Object 存入String 类型的 List 中,而你可以把任何类型的对象存入原始 List 中。使用泛型的带参数类型你不需要进行类型转换,但是对于原始类型,你则需要进行显式的类型转换。
List listOfRawTypes = new ArrayList(); listOfRawTypes.add("abc"); listOfRawTypes.add(123); //编译器允许这样 - 运行时却会出现异常 String item = (String) listOfRawTypes.get(0); //需要显式的类型转换 item = (String) listOfRawTypes.get(1); //抛 ClassCastException,因为 Integer 不能被转换为 String List listOfString = new ArrayList(); listOfString.add("abcd"); listOfString.add(1234); //编译错误,比在运行时抛异常要好 item = listOfString.get(0); //不需要显式的类型转换 - 编译器自动转换
通配符
通配符上界
常规使用
public class Test { public static void printIntValue(List extends Number> list) { for (Number number : list) { System.out.print(number.intValue()+" "); } System.out.println(); } public static void main(String[] args) { List integerList=new ArrayList(); integerList.add(2); integerList.add(2); printIntValue(integerList); List floatList=new ArrayList(); floatList.add((float) 3.3); floatList.add((float) 0.3); printIntValue(floatList); } }
输出:
2 2
3 0
非法使用
public class Test { public static void fillNumberList(List extends Number> list) { list.add(new Integer(0));//编译错误 list.add(new Float(1.0));//编译错误 } public static void main(String[] args) { List extends Number> list=new ArrayList(); list.add(new Integer(1));//编译错误 list.add(new Float(1.0));//编译错误 } }
List extends Number>可以代表 List或 List,为什么不能像其中加入 Integer或者 Float 呢?
首先,我们知道 List之中只能加入 Integer。并且如下代码是可行的:
List extends Number> list1=new ArrayList(); List extends Number> list2=new ArrayList();
那为什么对 List extends T>进行迭代可以呢,因为子类必定有父类相同的接口,这正是我们所期望的。
通配符下界
常规使用
public class Test { public static void fillNumberList(List super Number> list) { list.add(new Integer(0)); list.add(new Float(1.0)); } public static void main(String[] args) { List super Number> list=new ArrayList(); list.add(new Integer(1)); list.add(new Float(1.1)); } }
可以添加 Number 的任何子类,为什么呢?
List super Number>可以代表 List,其中 T 为 Number 父类,(虽然 Number 没有父类)。如果说,T 为 Number 的父类,我们想 List中加入 Number 的子类肯定是可以的。
非法使用
对 List superT>进行迭代是不允许的。为什么呢?你知道用哪种接口去迭代 List 吗?只有用 Object类的接口才能保证集合中的元素都拥有该接口,显然这个意义不大。其应用场景略。