泛型:
public static <T> T gMethod(List<T> list){.....}
这种语法和generic classes有相当程度的不同:泛型符号<T>必须加在class名称之后,却必须加在method名称(及回传型别)之前。
jdk1.5还允许将“不被method实际用到”的型别参数以符号‘?’表示,例如:
public static List<?> gMethod(List<?> list){ return list;//本例简单地原封不动传回 }
此例gMethod()接受一个List(无论其元素型别是什么),传回一个list(无论其元素型别是什么)。由于不存在(或说不在乎)型别参数(因为metho内根本不去用它),也就不必如平常一般在回传型别之前写出<T>来告知编译器了。
参数化型别的序列化:
将LikedList<Integer>得list序列化到文件中,然后读出,不过以下转换jdk1.5编译器会发出警告:
LinkedList<Integer> list1 = (Linkedlist<Integer>)in.readObject(); 告诉我们“unchecked cast”(jdk1.4则直接报错)
其他形式也不可以,比如:
LinkedList list1 = (LinkedList<Integer>)in.readObject();
只能写成这样:
LinkedList list1 = (LinkedList)in.readObject();
以上事实告诉我们,当object被写入文件,即失去其泛型型别参数(如果有的话)。因此读回的只是非泛型的class信息 。
如果上面的代码改写如下:
ArrayList list2 = (ArrayList)in.readObject();//可以顺利编译,在编译器的眼里是没问题的,但在执行时会出现异常,报类转换异常。
将对象序列化后,在序列化文件中记录的类的名称是java.util.LinkedList,并一并记录了元素型别java.lang.Integer(及其base class java.lang.Number).这些记录对于反序列化过程中恢复容器原型和内容有其必要,但无法让编译器推论当初使用的是LinkedList容器或是LinkedList<Integer>容器。
如果collection被参数化,则Iterator也应该使用相同的参数。
List list = new ArrayList(); list.add("1"); list.add("2"); list.add("3"); for(Iterator<String> itr = list.iterator();itr.hasNext();){ String s = itr.next(); System.out.println(s); }
以上代码编译和运行都没有问题。
List list = new ArrayList(); list.add(1);//加入的是数值,但在后面的iterator中,却期望的是String类型。 list.add("2"); list.add("3"); for(Iterator<String> itr = list.iterator();itr.hasNext();){ String s = itr.next(); System.out.println(s); }
以上代码编译没问题,但是在运行时出现类转换异常。所以在你将Iterator参数化得同时最好也参数化你的Collection,避免出现以上问题。
Map<String,List<List<int[]>>> map = getMap();
从这个map中取值会很抽象,int value = map.get(someKey).get(0).get(0)[0];
你无须对list做转换,而是让编译器来帮你拆解你的parameterized type。
Generic与类型转换
合法:
LinkedList<Integer> intList = new LinkedList<Integer>(); List<Integer> moreFloats = intList;
非法:
LinkedList<Number> numberList = intList;
分析(了解两件事情):
1,numberlList.add(1.2);//当从中取出并被当做一个int来使用时会导致一个ClassCastException
**在类型转换中要被考虑的所来自的基础类型,而不是参数类型**
2,“擦除”的概念。java中的generic是个编译期的程序,且所有的分类信息是在编译期被处理的。一旦class被编译过后,分类信息会被擦出掉。考虑下面两个声明:
List<Stirng> strings = new LinkedList<String>(); List<Integer> ints = new LinkedList<Integer>();
信息是用在编译期以执行类型检查,但分类信息在运行时就被舍弃了。因此,对jvm来说,这些声明就变成了:
List strings = new LinkedList(); List ints = new LinkedList();
现在没有类型参数了,在看看原来的代码 :
List<Integer> ints = new LinkedList<Integer>(); List<Number> nums = ints;
这在编译期看起来可能还OK,但在运行时它们是两个list,其中一个打算要转换成另一个,毫无任何的类型安全性。
抑制类型安全性:
List<Integer> ints = new LinkedList<Integer>(); //扩张它(为了向后兼容) List oldList = ints; //这一行应该是不合法的,但编译和运行后会过关。 oldList.add("hello");//我的想法:转换之后,它就不会有ints成员变量所具有的Integer的限制,List的元素默认是object了
ereasure移除了所有的参数化,只有在运行时,当要iterating该list来打印时,问题才会显露出来。
有时也会用到简单的旧式List,Map或其他没有被参数化的时候。这回引发unchecked错误,除非你才用了generic通配符。
在Eclipse下
List list = new ArrayList();//显示类型不确定的警告 解决方法: List<?> list = new ArrayList<?>();//现在的语法已经表明---任何类型都是可以接受的,且unchecked警告也不见了。
合法的写法:
List<Integer> list = new LinkedList<Integer>(); List<?> test = list;
因为你已经将list声明为List<?>,现在get()返回的是一个object,那已经非常接近java中的“unknown”。虽然是同样的类型,但这与一个仅能够使用Object的List<Object>是非常不同的,更诡异的事情发生在add()与其他采用符合该collection类型参数的method上面。因为编译器无法检查以确保类型安全性,所以它会拒绝对List<?>的add(),addAll()与set()所做的任何调用。换言之,将generic类型赋予通配符会使其基本上并成只读的。