泛型对于老代码的支持

Java的泛型设计成类型擦除的目的,很大一部分是为了兼容老老代码。如下的一段代码:

void setLabelTable(Dictionary table)

table的类型是非泛型的Dictionary,但是我们可以传入泛型的Dictionary:

Dictionary<Integer, Component> labelTable = new Hashtable<>();
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20, new JLabel(new ImageIcon("ten.gif")));
...
slider.setLabelTable(labelTable); // WARNING

注意,当向setLabelTable传入一个泛型的Dic,编译器会产生一个警告。

还有一种相反的情况,一个方法的返回类型是原始类型,但是可以分配给一个泛型类型持有:

Dictionary<Integer, Components> labelTable = slider.getLabelTable(); // WARNING

这种情况也会产生一个警告信息。

可以是用java注解来使这些警告消失掉:

@SuppressWarnings("unchecked")
Dictionary<Integer, Components> labelTable = slider.getLabelTable(); // No warning

或者注解在整个方法上面,使得方法内的所有警告消失:

@SuppressWarnings("unchecked")
public void configureSlider() { . . . }

泛型的使用限制

泛型的很多限制都是由于类型擦除机制带来的

泛型的类型参数不能是原始类型

Type Parameters Cannot Be Instantiated with Primitive Types

不能用Pair<double>,而只能使用Pair<Double>

运行时的类型查询与判断只能作用于非泛型类型,不支持泛型类的类型查询

Runtime Type Inquiry Only Works with Raw Types
由于Java虚拟机运行的代码都是没有泛型的,所以只能对非泛型类型进行类型查询与判断,如下代码是错误的:

if (a instanceof Pair<String>) // ERROR
if (a instanceof Pair<T>) // ERROR

只能对a instanceof Pair,不能判断泛型类。

下面这种情况会查询一个编译时的警告

Pair<String> p = (Pair<String>) a; // WARNING--can only test that a is a Pair

同样的,进行下面的判断,也只是对擦除后的类型进行的,与使用的什么类型参数无关:

Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass()) // they are equal

上面的比较返回的是true,因为他们都属于擦除后的类型Pair.class

不能创建泛型类的数组

You Cannot Create Arrays of Parameterized Types

下面这种代码是错误的:

Pair<String>[] table = new Pair<String>[10]; // ERROR

在类型擦除后,table的类型实际上是Pair[],可以将其转换为Object[]类型:

Object[] objarray = table;

由于数组是可以记住它内部元素的类型的,并且当你试图存储一个类型不匹配的元素时,会抛出ArrayStoreException异常。

那么像下面的代码:

objarray[0] = new Pair<Employee>();

首先,进行类型擦除,那么就变成了objarray[0] = new Pair();
但是objarray记住的内部元素类型应该是Pair, 与Pair不符,那么就会抛出异常。
由于这种情况,Java不允许new一个泛型类型的数组。

如果需要存储泛型类对象的集合,直接使用ArrayList即可,既安全又高效。

ArrayList<Pair<String>>

可变参数使用泛型类型

像下面这种泛型类型作为可变参数:

public static <T> void addAll(Collection<T> coll, T... ts)
{
for (t : ts) coll.add(t);
}

由于可变参数实际上是语法糖,就是一个数组,那么ts就是一个T[]数组,当我们使用这个addAll方法,如下:

Collection<Pair<String>> table = ...; 

Pair<String> pair1 = ...; 

Pair<String> pair2 = ...; 

addAll(table, pair1, pair2);

Java会将形参ts转换为Pair泛型类型数组:

Pair<String>[]

根据上一个限制,Java是不允许创建泛型类型的数组的。但是,在这里Java允许创建这种泛型类型数组。
这样传入泛型对象时,Java会报出警告信息,可以使用注解放在包含调用addAll方法的方法来去掉这种警告:

@SuppressWarnings("unchecked")

在Java7以后,可以使用新的注解@SafeVarargs来注解addAll方法本身,来去掉这种警告:

@SafeVarargs
public static <T> void addAll(Collection<T> coll, T... ts)

不能初始化泛型类类型参数

不能像如下代码使用来直接new一个类型参数:

new T(...), new T[...], or T.class.

如下在Pair< T >中直接new一个类型参数是非法的:

public Pair() { first = new T(); second = new T(); } // ERROR

因为类型擦除,会将T擦除为Object,那么上面的代码直接就是new Object(), 显然不是我们想要的。

workaround的方法是使用反射机制来new一个泛型类型的对象,使用时,需要多传入一个Class< T > 对象,如下:

public static <T> Pair<T> makePair(Class<T> cl)
{
try
{
return new Pair<>(cl.newInstance(), cl.newInstance())
}
catch (Exception ex)
{
return null;
}
}

上面的方法需要使用方传入一个泛型类型T的Class对象,然后使用Class的newInstance方法产生泛型类型的实例。

注意,Class自身就是泛型的,比如,String.class就是一个Class< String >的实例。

同样也不允许直接new一个泛型类型数组,如下代码:

// ERROR
public static <T extends Comparable> T[] minmax(T[] a)
{
T[] mm = new T[2];
. . .
}

上面的代码,擦除到边界后变为了Comparable[] mm = new Comparable[2]; 这也不是我们想要的,如果你只想将泛型类数组当成一个内部使用的字段,那么可以想下面的ArrayList< E> 一样直接内部使用Object[] 即可:

public class ArrayList<E>
{
private Object[] elements;
... @SuppressWarnings("unchecked")
public E get(int n)
{
return (E)elements[n];
} public void set(int n, E e)
{
// no cast needed
elements[n] = e;
}
}

当然,如果需要在一个方法中返回泛型数组的话,下面的代码是错误的:

public static <T extends Comparable> T[] minmax(T... a)
{
Object[] mm = new Object[2];
...
return (T[]) mm; // compiles with warning
}

因为编译器会擦除到Comparable,返回的数组就不是你想要的泛型类型数组了,解决方法如下:

public static <T extends Comparable> T[] minmax(T... a)
{
T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);
...
}

使用Array的newIntance方法来生成数组。

类的静态字段不能使用泛型类

下面的静态字段不能使泛型类:

public class Singleton<T>
{
private static T singleInstance; // ERROR
public static T getSingleInstance() // ERROR
{
if (singleInstance == null)
{
construct new instance of T
return singleInstance;
}
}
}

因为类型擦除,singleInstance不管传入什么,都是一个Object类型,所以没有意义。

不能抛出泛型类异常,也不能捕获泛型类异常的实例

下面的代码,直接扩展Exception是不允许的:

public class Problem<T> extends Exception { /* . . . */ } // ERROR-- can't extend Throwable

下面的代码,也不能catch一个泛型类型的异常:

public static <T extends Throwable> void doWork(Class<T> t)
{
try
{
do work
}
catch (T e) // ERROR--can't catch type variable
{
Logger.global.info(...)
}
}

但是泛型类型参数作为方法名后的throws T是允许的,像下面的代码:

public static <T extends Throwable> void doWork(T t) throws T // OK
{
try
{
do work
}
catch (Throwable realCause)
{
t.initCause(realCause);
throw t;
}
}

注意泛型参数被擦除后带来的冲突

如下代码:

public class Pair<T>
{
public boolean equals(T value)
{
return first.equals(value) && second.equals(value);
}
...
}

当我们定义了一个Pair< String>泛型类,如果没有擦除机制,那么Pair< String>类里面会有两个如下的equals方法,一个定义在此类中,另一个定义在隐式继承类Object中:

boolean equals(String) // defined in Pair<T>
boolean equals(Object) // inherited from Object

但是,在擦除掉Pair< String>中的String后,实际上下面这个equals方法:

boolean equals(T)

擦除后变为了下面这样:

boolean equals(Object)

那么这个equals方法就覆盖掉了Object类中的equals方法,也就发生了冲突。解决方法很简单,就是重命名Pair< T>中的equals方法。

不允许一个类继承自两个只是泛型类参数不同的类

像下面的GregorianCalendar类,间接继承自Comparable和Comparable两个只是泛型类参数不同的同一个接口是不允许的:

class Calendar implements Comparable<Calendar> { . . . }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>
{ . . . } // ERROR

泛型类型的继承规则

如果Employee是父类,Manager继承自Employee,那么这两个类作为同一个泛型类的类型参数时,这两个泛型类没有任何关系

注意泛型类与Java数组之间的一个重要的区别,可以将一个Manager[]数组赋给一个类型为Employee[]数组的变量:

Manager[] managerBuddies = {ceo, cfo};
Employee[] employeeBuddies = managerBuddies; //OK

然而,数组带有特别的保护,如果试图将一个低级别的Employee存储到employeeBuddies[0], 虚拟机将会抛出ArrayStoreException异常。

你总是可以将一个泛型类型转换为它的原始类型,例如,Pair< Employee>是原始类型

Pair的一个子类,这种转换能力是用来兼容Java老代码的。

最后,泛型类可以继承或者实现其他泛型类,在这一点上,它与普通类没有区别。例如,ArrayList< T>实现了接口List< T>,那么ArrayList< Manager>就可以转换为List< Manager>,但是ArrayList< Manager>与ArrayList< Employee>和List< Employee>无关,不能转换

:first-child{margin-top:0!important}img.plugin{box-shadow:0 1px 3px rgba(0,0,0,.1);border-radius:3px}iframe{border:0}figure{-webkit-margin-before:0;-webkit-margin-after:0;-webkit-margin-start:0;-webkit-margin-end:0}kbd{border:1px solid #aaa;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-moz-box-shadow:1px 2px 2px #ddd;-webkit-box-shadow:1px 2px 2px #ddd;box-shadow:1px 2px 2px #ddd;background-color:#f9f9f9;background-image:-moz-linear-gradient(top,#eee,#f9f9f9,#eee);background-image:-o-linear-gradient(top,#eee,#f9f9f9,#eee);background-image:-webkit-linear-gradient(top,#eee,#f9f9f9,#eee);background-image:linear-gradient(top,#eee,#f9f9f9,#eee);padding:1px 3px;font-family:inherit;font-size:.85em}.oembeded .oembed_photo{display:inline-block}img[data-echo]{margin:25px 0;width:100px;height:100px;background:url(../img/ajax.gif) center center no-repeat #fff}.spinner{display:inline-block;width:10px;height:10px;margin-bottom:-.1em;border:2px solid rgba(0,0,0,.5);border-top-color:transparent;border-radius:100%;-webkit-animation:spin 1s infinite linear;animation:spin 1s infinite linear}.spinner:after{content:'';display:block;width:0;height:0;position:absolute;top:-6px;left:0;border:4px solid transparent;border-bottom-color:rgba(0,0,0,.5);-webkit-transform:rotate(45deg);transform:rotate(45deg)}@-webkit-keyframes spin{to{-webkit-transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}p.toc{margin:0!important}p.toc ul{padding-left:10px}p.toc>ul{padding:10px;margin:0 10px;display:inline-block;border:1px solid #ededed;border-radius:5px}p.toc li,p.toc ul{list-style-type:none}p.toc li{width:100%;padding:0;overflow:hidden}p.toc li a::after{content:"."}p.toc li a:before{content:"• "}p.toc h5{text-transform:uppercase}p.toc .title{float:left;padding-right:3px}p.toc .number{margin:0;float:right;padding-left:3px;background:#fff;display:none}input.task-list-item{margin-left:-1.62em}.markdown{font-family:"Hiragino Sans GB","Microsoft YaHei",STHeiti,SimSun,"Lucida Grande","Lucida Sans Unicode","Lucida Sans",'Segoe UI',AppleSDGothicNeo-Medium,'Malgun Gothic',Verdana,Tahoma,sans-serif;padding:20px}.markdown a{text-decoration:none;vertical-align:baseline}.markdown a:hover{text-decoration:underline}.markdown h1{font-size:2.2em;font-weight:700;margin:1.5em 0 1em}.markdown h2{font-size:1.8em;font-weight:700;margin:1.275em 0 .85em}.markdown h3{font-size:1.6em;font-weight:700;margin:1.125em 0 .75em}.markdown h4{font-size:1.4em;font-weight:700;margin:.99em 0 .66em}.markdown h5{font-size:1.2em;font-weight:700;margin:.855em 0 .57em}.markdown h6{font-size:1em;font-weight:700;margin:.75em 0 .5em}.markdown h1+p,.markdown h1:first-child,.markdown h2+p,.markdown h2:first-child,.markdown h3+p,.markdown h3:first-child,.markdown h4+p,.markdown h4:first-child,.markdown h5+p,.markdown h5:first-child,.markdown h6+p,.markdown h6:first-child{margin-top:0}.markdown hr{border:1px solid #ccc}.markdown p{margin:1em 0;word-wrap:break-word}.markdown ol{list-style-type:decimal}.markdown li{display:list-item;line-height:1.4em}.markdown blockquote{margin:1em 20px}.markdown blockquote>:first-child{margin-top:0}.markdown blockquote>:last-child{margin-bottom:0}.markdown blockquote cite:before{content:'\2014 \00A0'}.markdown .code{border-radius:3px;word-wrap:break-word}.markdown pre{border-radius:3px;word-wrap:break-word;border:1px solid #ccc;overflow:auto;padding:.5em}.markdown pre code{border:0;display:block}.markdown pre>code{font-family:Consolas,Inconsolata,Courier,monospace;font-weight:700;white-space:pre;margin:0}.markdown code{border-radius:3px;word-wrap:break-word;border:1px solid #ccc;padding:0 5px;margin:0 2px}.markdown img{max-width:100%}.markdown mark{color:#000;background-color:#fcf8e3}.markdown table{padding:0;border-collapse:collapse;border-spacing:0;margin-bottom:16px}.markdown table tr td,.markdown table tr th{border:1px solid #ccc;margin:0;padding:6px 13px}.markdown table tr th{font-weight:700}.markdown table tr th>:first-child{margin-top:0}.markdown table tr th>:last-child{margin-bottom:0}.markdown table tr td>:first-child{margin-top:0}.markdown table tr td>:last-child{margin-bottom:0}.MathJax_Hover_Frame{border-radius:.25em;-webkit-border-radius:.25em;-moz-border-radius:.25em;-khtml-border-radius:.25em;box-shadow:0 0 15px #83A;-webkit-box-shadow:0 0 15px #83A;-moz-box-shadow:0 0 15px #83A;-khtml-box-shadow:0 0 15px #83A;border:1px solid #A6D!important;display:inline-block;position:absolute}.MathJax_Hover_Arrow{position:absolute;width:15px;height:11px;cursor:pointer}#MathJax_About{position:fixed;left:50%;width:auto;text-align:center;border:3px outset;padding:1em 2em;background-color:#DDD;color:#000;cursor:default;font-family:message-box;font-size:120%;font-style:normal;text-indent:0;text-transform:none;line-height:normal;letter-spacing:normal;word-spacing:normal;word-wrap:normal;white-space:nowrap;float:none;z-index:201;border-radius:15px;-webkit-border-radius:15px;-moz-border-radius:15px;-khtml-border-radius:15px;box-shadow:0 10px 20px gray;-webkit-box-shadow:0 10px 20px gray;-moz-box-shadow:0 10px 20px gray;-khtml-box-shadow:0 10px 20px gray;filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')}.MathJax_Menu{position:absolute;background-color:#fff;color:#000;width:auto;padding:5px 0;border:1px solid #CCC;margin:0;cursor:default;font:menu;text-align:left;text-indent:0;text-transform:none;line-height:normal;letter-spacing:normal;word-spacing:normal;word-wrap:normal;white-space:nowrap;float:none;z-index:201;border-radius:5px;-webkit-border-radius:5px;-moz-border-radius:5px;-khtml-border-radius:5px;box-shadow:0 10px 20px gray;-webkit-box-shadow:0 10px 20px gray;-moz-box-shadow:0 10px 20px gray;-khtml-box-shadow:0 10px 20px gray;filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')}.MathJax_MenuItem{padding:1px 2em;background:0 0}.MathJax_MenuArrow{position:absolute;right:.5em;color:#666}.MathJax_MenuActive .MathJax_MenuArrow{color:#fff}.MathJax_MenuArrow.RTL{left:.5em;right:auto}.MathJax_MenuCheck{position:absolute;left:.7em}.MathJax_MenuCheck.RTL{right:.7em;left:auto}.MathJax_MenuRadioCheck{position:absolute;left:.7em}.MathJax_MenuRadioCheck.RTL{right:.7em;left:auto}.MathJax_MenuLabel{padding:1px 2em 3px 1.33em;font-style:italic}.MathJax_MenuRule{border-top:1px solid #DDD;margin:4px 3px}.MathJax_MenuDisabled{color:GrayText}.MathJax_MenuActive{background-color:#606872;color:#fff}.MathJax_Menu_Close{position:absolute;width:31px;height:31px;top:-15px;left:-15px}#MathJax_Zoom{position:absolute;background-color:#F0F0F0;overflow:auto;display:block;z-index:301;padding:.5em;border:1px solid #000;margin:0;font-weight:400;font-style:normal;text-align:left;text-indent:0;text-transform:none;line-height:normal;letter-spacing:normal;word-spacing:normal;word-wrap:normal;white-space:nowrap;float:none;box-shadow:5px 5px 15px #AAA;-webkit-box-shadow:5px 5px 15px #AAA;-moz-box-shadow:5px 5px 15px #AAA;-khtml-box-shadow:5px 5px 15px #AAA;filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')}#MathJax_ZoomOverlay{position:absolute;left:0;top:0;z-index:300;display:inline-block;width:100%;height:100%;border:0;padding:0;margin:0;background-color:#fff;opacity:0;filter:alpha(opacity=0)}#MathJax_ZoomFrame{position:relative;display:inline-block;height:0;width:0}#MathJax_ZoomEventTrap{position:absolute;left:0;top:0;z-index:302;display:inline-block;border:0;padding:0;margin:0;background-color:#fff;opacity:0;filter:alpha(opacity=0)}.MathJax_Preview{color:#888}#MathJax_Message{position:fixed;left:1px;bottom:2px;background-color:#E6E6E6;border:1px solid #959595;margin:0;padding:2px 8px;z-index:102;color:#000;font-size:80%;width:auto;white-space:nowrap}#MathJax_MSIE_Frame{position:absolute;top:0;left:0;width:0;z-index:101;border:0;margin:0;padding:0}.MathJax_Error{color:#C00;font-style:italic}footer{position:fixed;font-size:.8em;text-align:right;bottom:0;margin-left:-25px;height:20px;width:100%}
-->

最新文章

  1. 10、ASP.NET MVC入门到精通——Model(模型)和验证
  2. JAVA(IO流)文件复制
  3. MFC之键盘和键盘消息处理
  4. c#.net对excel的操作——创建一个excel报表两个sheet就是2个表分别添加内容
  5. 修改WordPress中上传附件2M大小限制的方法/php+iis上传附件默认大小修改方法
  6. MVC 5 + EF6 入门完整教程14 -- 动态生成面包屑导航
  7. POJ 1151 / HDU 1542 Atlantis 线段树求矩形面积并
  8. http响应状态码301和302
  9. Swagger-UI 基于REST的API测试/文档类插件
  10. About gpref O(n2) --&gt; O(1)
  11. 【HDU3487】【splay分裂合并】Play with Chain
  12. 自定义ModelValidatorProvider
  13. js 获取页面可视区域宽高
  14. python 中常见绘图属性
  15. 关于网站的一些js和css常见问题的记录
  16. Flack--SQLAlchemy
  17. 【Visual Studio 扩展工具】如何在ComponentOneFlexGrid树中显示RadioButton
  18. .NET MVC 学习笔记(七)— 控制input控件
  19. 阿里八八Alpha阶段Scrum(10/12)
  20. dubbox实现REST服务

热门文章

  1. 【java规则引擎】模拟rete算法的网络节点以及匹配过程
  2. swing之JDialog
  3. 利用MsChart控件绘制多曲线图表
  4. 得到properties配置文件内容
  5. rem怎么计算
  6. Celery-4.1 用户指南: Configuration and defaults (配置和默认值)
  7. [转]RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
  8. 问题:oracle字符串函数;结果:Oracle字符串函数
  9. 部署和调优 3.3 dns安装配置-3
  10. DAY11-MYSQL数据备份、pymysql模块