Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at generic.BeforeGeneric.main(BeforeGeneric.java:24)
public class GenericType { public static void main(String[] args) { ArrayList stringValues=new ArrayList(); stringValues.add("str"); stringValues.add(1); //编译错误 } }
现在,如果我们向ArrayList添加Integer类型的对象,将会出现编译错误。
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method add(int, String) in the type ArrayList is not applicable for the arguments (int) at generic.GenericType.main(GenericType.java:8)
编译器会自动帮我们检查,避免向集合中插入错误类型的对象,从而使得程序具有更好的安全性。
总之,泛型通过类型参数使得我们的程序具有更好的可读性和安全性。
Java泛型的实现原理
擦除
public class GenericType { public static void main(String[] args) { ArrayList arrayString=new ArrayList(); ArrayList arrayInteger=new ArrayList(); System.out.println(arrayString.getClass()==arrayInteger.getClass()); } }
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.add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类 int b=Test.add(1, 2.2);//编译错误,指定了Integer,不能为Float Number c=Test.add(1, 2.2); //指定为Number,所以可以为Integer和Float } //这是一个简单的泛型方法 public static T add(T x,T y){ return y; } }
限定通配符对类型进行了限制。有两种限定通配符,一种是 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或List。
5. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用T, E or K,V等被广泛认可的类型占位符。泛型方法的例子请参阅Java集合类框架。最简单的情况下,一个泛型方法可能会像这样:
public V put(K key, V value) { return cache.put(key, value); }
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();
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的子类肯定是可以的。