下面是一个直接依赖于Sun API和反射的小实验(也就是说,它使用反射来处理实现反射的类):
import java.lang.Class;
import java.lang.reflect.*;
import java.util.Arrays;
import sun.reflect.generics.reflectiveObjects.*;
class Types {
private static Constructor<ParameterizedTypeImpl> PARAMETERIZED_TYPE_CONS =
((Constructor<ParameterizedTypeImpl>)
ParameterizedTypeImpl
.class
.getDeclaredConstructors()
[0]
);
static {
PARAMETERIZED_TYPE_CONS.setAccessible(true);
}
/**
* Helper method for invocation of the
*`ParameterizedTypeImpl` constructor.
*/
public static ParameterizedType parameterizedType(
Class<?> raw,
Type[] paramTypes,
Type owner
) {
try {
return PARAMETERIZED_TYPE_CONS.newInstance(raw, paramTypes, owner);
} catch (Exception e) {
throw new Error("TODO: better error handling", e);
}
}
// (similarly for `GenericArrayType`, `WildcardType` etc.)
/** Substitution of type variables. */
public static Type substituteTypeVariable(
final Type inType,
final TypeVariable<?> variable,
final Type replaceBy
) {
if (inType instanceof TypeVariable<?>) {
return replaceBy;
} else if (inType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) inType;
return parameterizedType(
((Class<?>) pt.getRawType()),
Arrays.stream(pt.getActualTypeArguments())
.map((Type x) -> substituteTypeVariable(x, variable, replaceBy))
.toArray(Type[]::new),
pt.getOwnerType()
);
} else {
throw new Error("TODO: all other cases");
}
}
// example
public static void main(String[] args) throws InstantiationException {
// type in which we will replace a variable is `List<E>`
Type t =
java.util.LinkedList
.class
.getGenericInterfaces()
[0];
// this is the variable `E` (hopefully, stability not guaranteed)
TypeVariable<?> v =
((Class<?>)
((ParameterizedType) t)
.getRawType()
)
.getTypeParameters()
[0];
// This should become `List<String>`
Type s = substituteTypeVariable(t, v, String.class);
System.out.println("before: " + t);
System.out.println("after: " + s);
}
}
E
通过
String
在里面
List<E>
before: java.util.List<E>
after: java.util.List<java.lang.String>
主要思路如下:
-
得到
sun.reflect.generics.reflectiveObjects.XyzImpl
班级
-
找到他们的构造器,确保他们
accessible
-
包装构造函数
.newInstance
-
在一个简单的递归方法中使用helper方法
substituteTypeVariable
重建
Type
我并没有实现每一个案例,但它也应该可以处理更复杂的嵌套类型(因为
替代变量
).
编译器并不真正喜欢这种方法,它会生成有关使用内部Sun API的警告:
但是,有一个
@SuppressWarnings
for that
.
上面的Java代码是通过翻译下面的Scala片段获得的(这就是为什么Java代码看起来有点奇怪,并不完全是Java惯用的原因):
object Types {
import scala.language.existentials // suppress warnings
import java.lang.Class
import java.lang.reflect.{Array => _, _}
import sun.reflect.generics.reflectiveObjects._
private val ParameterizedTypeCons =
classOf[ParameterizedTypeImpl]
.getDeclaredConstructors
.head
.asInstanceOf[Constructor[ParameterizedTypeImpl]]
ParameterizedTypeCons.setAccessible(true)
/** Helper method for invocation of the `ParameterizedTypeImpl` constructor. */
def parameterizedType(raw: Class[_], paramTypes: Array[Type], owner: Type)
: ParameterizedType = {
ParameterizedTypeCons.newInstance(raw, paramTypes, owner)
}
// (similarly for `GenericArrayType`, `WildcardType` etc.)
/** Substitution of type variables. */
def substituteTypeVariable(
inType: Type,
variable: TypeVariable[_],
replaceBy: Type
): Type = {
inType match {
case v: TypeVariable[_] => replaceBy
case pt: ParameterizedType => parameterizedType(
pt.getRawType.asInstanceOf[Class[_]],
pt.getActualTypeArguments.map(substituteTypeVariable(_, variable, replaceBy)),
pt.getOwnerType
)
case sthElse => throw new NotImplementedError()
}
}
// example
def main(args: Array[String]): Unit = {
// type in which we will replace a variable is `List<E>`
val t =
classOf[java.util.LinkedList[_]]
.getGenericInterfaces
.head
// this is the variable `E` (hopefully, stability not guaranteed)
val v =
t
.asInstanceOf[ParameterizedType]
.getRawType
.asInstanceOf[Class[_]] // should be `List<E>` with parameter
.getTypeParameters
.head // should be `E`
// This should become `List<String>`
val s = substituteTypeVariable(t, v, classOf[String])
println("before: " + t)
println("after: " + s)
}
}