转自:
http://www.dongliu.net/post/5896921377931264
都Java 1.5在加入泛型支持时,为了保持兼容,采用的是擦除法实现,泛型的定义只在编译的时候有效,编译之后是没有保留泛型的类型信息的,运行时所有的instance都当作限界类型来使用,没有指定限界类型是则是当作Object类型。所以,通常是无法在运行时获得泛型的类型活实例化泛型类的,如下面的代码都无法通过编译:
public
class
Test
<
T
>
{
public
Test
(){
T
t
=
new
T
();
// error
Class
<
T
>
clazz
=
T
.
class
;
//error
T
[]
ts
=
new
T
[
10
];
//error
}
}
然而,擦除法的实现存在一些特列,在这些特例情况下,Java会记录泛型的类型信息,并且可以通过Api来获取的。比如在各种持久化框架中广泛使用的BaseDao泛型类的写法,就利用一个泛型继承的特例:
public
class
BaseDAO
<
U
>
{
private
Class
<
U
>
entityClass
;
protected
BaseDAO
()
{
// 获取泛型类型
Type
type
=
getClass
().
getGenericSuperclass
();
Type
[]
trueType
=
((
ParameterizedType
)
type
).
getActualTypeArguments
();
this
.
entityClass
=
(
Class
<
U
>)
trueType
[
0
];
}
public
U
getById
(
Serializable
id
){
PersistenceManager
pm
=
getPersistenceManager
();
pm
.
setDetachAllOnCommit
(
true
);
U
object
;
try
{
object
=
pm
.
getObjectById
(
entityClass
,
id
);
}
catch
(
JDOObjectNotFoundException
e
)
{
object
=
null
;
}
finally
{
pm
.
close
();
}
return
object
;
}
}
public
class
TagDAO
extends
BaseDAO
<
Tag
>{
}
代码的关键是getGenericSuperclass方法,这个方法返回的类型为ParameterizedType,里面可以通过getActualTypeArguments来获取泛型的真正类型。这样的特例是违背了擦除法的实现思想的,这也是Java为保证类型系统完整而不得不做的妥协。
Jackson json中反序列化时,可以通过TypeReference传递容器中的泛型类型,也是利用了泛型继承的特例:
Map
<
Integer
,
ApkBean
>
map
=
mapper
.
readValue
(
jsonData
,
new
TypeReference
<
Map
<
Integer
,
ApkBean
>>(){});
TypeReference是一个泛型抽象类,在readValue的第二个方法中,传入了TypeReference的一个匿名子类实例,由此带入了Map的泛型信息。由于是两级泛型的嵌套,具体的情况其实更复杂一些,有兴趣的可以看看Jackson代码中TypeReference和TypeFactory的实现。
除了泛型继承这种情况之外,还有另外两个特例,也可以获取ParameterizedType类型,继而获取到泛型的类型。一个是类的field可以通过getGenericType来获取:
public
class
Test
{
private
Map
<
String
,
Number
>
map
;
public
static
void
main
(
String
[]
args
)
throws
NoSuchFieldException
{
Class
<?>
clazz
=
Test
.
class
;
Field
field
=
clazz
.
getDeclaredField
(
“map”
);
//取得泛型类型
Type
type
=
field
.
getGenericType
();
ParameterizedType
ptype
=
(
ParameterizedType
)
type
;
System
.
out
.
println
(
ptype
.
getActualTypeArguments
()[
0
]);
System
.
out
.
println
(
ptype
.
getActualTypeArguments
()[
1
]);
}
}
另外就是方法的泛型参数可以通过getGenericParameterTypes来获取:
public
class
Test
{
public
static
void
main
(
String
[]
args
)
throws
NoSuchMethodException
{
Class
<?>
clazz
=
Test
.
class
;
Method
method
=
clazz
.
getDeclaredMethod
(
“getGenericSample”
,
Collection
.
class
);
//取得泛型类型参数集
Type
[]
type
=
method
.
getGenericParameterTypes
();
ParameterizedType
ptype
=
(
ParameterizedType
)
type
[
0
];
type
=
ptype
.
getActualTypeArguments
();
System
.
out
.
println
(
type
[
0
]);
}
public
void
getGenericSample
(
Collection
<
Number
>
collection
){
}
}
这两种特例的实用价值比较小,实际的代码hack中也比较少见。