这篇文章主要介绍“为什么不要在新代码中使用原生态类型”,在日常操作中,相信很多人在为什么不要在新代码中使用原生态类型问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”为什么不要在新代码中使用原生态类型”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
一些泛型的专业术语
泛型类或泛型接口:声明中具有一个或多个类型参数(type parameter)的类或者接口,统称为泛型。eg,jdk1.5之后,List 接口只有单个类型参数E,表示列表的元素类型,所以他的接口名称应该是List<E>,但是人们常常把它简称为List。
参数化的类型(parameterized type),构成格式是:类或接口的名称 + 尖括号(<>)将泛型形式参数的实际类型参数列表括起来。
每个泛型都定义类一个 原生态类型(raw type),即不带任何实际类型参数的泛型名称。eg,List<E> 对应的原生态类型是List。原生态类型就相当于从类型声明中删除了泛型信息。
使用泛型进行编码,有两个好处:
下面我们通过一个例子阐述清楚,代码如下:
/** * @exception ClassCastException */ private static void testGenericeBeforejdk5() { Collection stamps = new ArrayList(); stamps.add(new Coin()); for (Iterator i = stamps.iterator(); i.hasNext();){ Stamp next = (Stamp)i.next(); //ClassCastException } } private static void testGenericeAfterjdk5() { Collection<Stamp> stamps = new ArrayList(); stamps.add(new Coin());//编译器告诉我们错误 } // 两个测试内部类 static class Stamp{} static class Coin{}
Exception in thread "main" java.lang.ClassCastException: effectivejava.no23.TestGeneric$Coin cannot be cast to effectivejava.no23.TestGeneric$Stamp at effectivejava.no23.TestGeneric.testGenericeBeforejdk5(TestGeneric.java:26) at effectivejava.no23.TestGeneric.main(TestGeneric.java:14)
testGenericeAfterjdk5()方法里,我们使用了泛型定义了集合的参数类型。通过这条声明,编译器知道 stamps 集合应该只包含Stamp 实例,并给以保证。因此在代码开发时,我们不小心将一个coin 实例放进stamps集合时,编译器会及时提醒我们并产生一条编译错误信息,准确告知程序员哪里出现错误。
Error:(20, 28) java: 不兼容的类型: effectivejava.no23.TestGeneric.Coin无法转换为effectivejava.no23.TestGeneric.Stamp
通过比较,我们还能发现,集合使用泛型,从集合中遍历元素时不需要再进行手工转换了。
其一、使用原生态类型,会失掉泛型在安全性和其他表述性方面的优势。
其二、原生态类型List 和 参数化类型List<Object>有区别。
下面通过一个例子解读两者的区别:
private static void testSubTyping() { List<String> strings = new ArrayList<String>(); unsafeAdd(strings, new Integer(110)); String s = strings.get(0); // exception while run the method; } /** * 方法使用了原生态类型,所以可以编译。 */ private static void unsafeAdd(List list, Object o) { list.add(0); } /** * 方法使用了List<Object>替代原生态类型,所以编译不会通过。 */ private static void unsafeAddV2(List<Object> list, Object o) { list.add(0); }
结论:使用List 这样的原生态类型会丢掉类型安全性,但是使用List<Object> 这样的参数化类型则不会。
不要在新代码中使用原生态类型,这条规则有两个小小的例外,原因是:泛型信息可以在运行时被编译器擦除了。
在类文字(class literal)中必须使用原生态类型,规范不允许使用参数化类型(但允许数组类型和基本类型)[JLS,15.8.2]
ClassLiteral:
TypeName {[ ]} . class
NumericType {[ ]} . class
boolean {[ ]} . class
void . class
A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a '.' and the token class.
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.8.2
private static void testInstanceOfInvalidOnGeneric(Object o) { if (o instanceof Set){ //一旦确定o是个Set,必须转换为通配符类型Set<?>,而不是原生态类型Set。这样能避免后续代码出现运行时异常。 Set<?> set = (Set<?>) o; } }
术语 | 示例 | 所在条目 |
参数化的类型 | List<String> | 23 |
实际类型参数 | String | 23 |
泛型 | List<E> | 23 |
形式类型参数 | E | 23 |
无限制通配符类型参数 | List<?> | 23 |
原生态类型参数 | List | 23 |
有限制类型参数 | List<E extends Number> | 26 |
递归类型限制 | List<T extends Comparable<T>> | 27 |
有限制通配符类型参数 | List<? extends Number> | 28 |
泛型方法 | static <E> List<E> asList(E[] a) | 27 |
类型令牌 | String.class | 29 |
到此,关于“为什么不要在新代码中使用原生态类型”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。