Java反思:如何从ParameterizedType.getActualTypeArguments()数组中获取Class<T>的实例?
Java Reflection: How to obtain an instance of Class<T> from the ParameterizedType.getActualTypeArguments() array?
我想做的是使用
获取字段的通用类型参数
Type type = f.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
// ...
Class<?> typearg0 = ??? // ptype.getActualTypeArguments()[0]
}
(其中 f
是 java.lang.Field
的一个实例)。我需要 Class<?>
的实例稍后执行 Foo.class.isAssignableFrom(typearg0)
检查,它只需要 Class<T>
es 作为参数。同时,我知道(或期望)说 typearg0
也是参数化类型 以及 ,所以有一个矛盾: typearg0
不能是 Class<?>
和 ParameterizedType
同时存在,因为 Class<?>
本身就是一个 class 并没有实现 ParameterizedType
.
我可以通过任何其他方式实现 isAssignableFrom()
检查吗?还是我的目标通常无法实现?
非常感谢!
编辑:
这是一个例子:
假设您期望 类型为Map<Foo, Set<Bar>>
的字段,但您得到的只是一个java.lang.reflect.Field
实例。现在,您不希望它 精确地 匹配 Map<Foo, Set<Bar>>
,您只希望各自的类型是 实例 of Map
或 Set
。这不是严格的规范检查,更像是“健全性检查”。我会尝试这样检查:
- 获取字段类型为 class (
.getType()
)
- 检查
Map
是否可以从字段类型 分配
- 获取字段类型作为(可能)参数化类型(
.getGenericType()
)
- 检查第一个通用参数是否为
Foo
(这应该是完全匹配)
- 获取第二个泛型参数作为class
- 检查
Set
是否可以从此嵌套类型赋值
- 检查嵌套类型本身是否是一个
ParameterizedType
并转换它
- 检查嵌套类型的第一个泛型参数是否为
Bar
(这应该是完全匹配)
但是不知道怎么实现5and/or6.
(使用 java 11)
就像您对 Map
所做的那样,Set
很可能是 ParameterizedType
的一个实例,因此您可以检查它然后再进行转换。
从那个 ParameterizedType
(代表 Set<Something>
),你可以使用 getRawType
得到一个 Set
。那也是一个 Type
—— 但它应该可以安全地转换为 Class
。以防万一,您可以使用回退并使用其名称加载 class。
final Type valueType = mapType.getActualTypeArguments()[1];
final ParameterizedType pValueType = (ParameterizedType) valueType;
final Class<?> valueClass = pValueType.getRawType() instanceof Class ?
((Class<?>) pValueType.getRawType()) :
// Should be castable, but just in case it's not, fallback
getClass().getClassLoader().loadClass(
pValueType.getRawType().getTypeName()
);
if (Set.class.isAssignableFrom(valueClass)) { /* do something*/ }
这是完整的 runnable example。
class Foo {}
class Bar {}
interface MyValidMap extends Map<Foo, Set<Bar>> {}
public class Application {
public final HashMap<Foo, HashSet<Bar>> good = null;
public final Map<Foo, Set<Bar>> better = null;
public final String notAMap = null;
public final Map<String, Set<Bar>> badKey = null;
public final Map<Foo, List<String>> badValue = null;
public final Map<Foo, Set<String>> badSetElems = null;
public final MyValidMap noTypeParamMap = null;
public static void main(String[] args) throws Exception {
for (Field field : Application.class.getFields()) {
System.out.println(field.getName() + " - " + fieldMatches(field));
}
}
private static String fieldMatches(Field mapField) throws Exception {
if (!Map.class.isAssignableFrom(mapField.getType())) {
return "Field is not a Map";
}
if (!(mapField.getGenericType() instanceof ParameterizedType)) {
// We know it's a map, but it doesn't have type params. Probably something
// like this: class MyStringMap implements Map<String, String>. You can do
// something with getGenericInterfaces() but this seems so unlikely that
// it could just be ignored.
return "TODO";
}
final ParameterizedType mapType = (ParameterizedType) mapField.getGenericType();
final Type keyType = mapType.getActualTypeArguments()[0];
final Type valueType = mapType.getActualTypeArguments()[1];
if (Foo.class != keyType) {
return "Map's key type is not Foo";
}
if (!(valueType instanceof ParameterizedType)) {
// Same problem as above. May be a Set without type params
return "Map's value is (probably) not a Set";
}
final ParameterizedType pValueType = (ParameterizedType) valueType;
final Class<?> valueClass = pValueType.getRawType() instanceof Class ?
((Class<?>) pValueType.getRawType()) :
Application.class.getClassLoader().loadClass(
pValueType.getRawType().getTypeName()
);
if (!Set.class.isAssignableFrom(valueClass)) {
return "Map's value is not a Set";
}
final Type setElemType = pValueType.getActualTypeArguments()[0];
if (setElemType != Bar.class) {
return "Set's elements are not Bars";
}
return "Looks good";
}
}
输出:
good - Looks good
better - Looks good
notAMap - Field is not a Map
badKey - Map's key type is not Foo
badValue - Map's value is (probably) not a Set
badSetElems - Set's elements are not Bars
noTypeParamMap - TODO
我想做的是使用
获取字段的通用类型参数Type type = f.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
// ...
Class<?> typearg0 = ??? // ptype.getActualTypeArguments()[0]
}
(其中 f
是 java.lang.Field
的一个实例)。我需要 Class<?>
的实例稍后执行 Foo.class.isAssignableFrom(typearg0)
检查,它只需要 Class<T>
es 作为参数。同时,我知道(或期望)说 typearg0
也是参数化类型 以及 ,所以有一个矛盾: typearg0
不能是 Class<?>
和 ParameterizedType
同时存在,因为 Class<?>
本身就是一个 class 并没有实现 ParameterizedType
.
我可以通过任何其他方式实现 isAssignableFrom()
检查吗?还是我的目标通常无法实现?
非常感谢!
编辑:
这是一个例子:
假设您期望 类型为Map<Foo, Set<Bar>>
的字段,但您得到的只是一个java.lang.reflect.Field
实例。现在,您不希望它 精确地 匹配 Map<Foo, Set<Bar>>
,您只希望各自的类型是 实例 of Map
或 Set
。这不是严格的规范检查,更像是“健全性检查”。我会尝试这样检查:
- 获取字段类型为 class (
.getType()
) - 检查
Map
是否可以从字段类型 分配
- 获取字段类型作为(可能)参数化类型(
.getGenericType()
) - 检查第一个通用参数是否为
Foo
(这应该是完全匹配) - 获取第二个泛型参数作为class
- 检查
Set
是否可以从此嵌套类型赋值 - 检查嵌套类型本身是否是一个
ParameterizedType
并转换它 - 检查嵌套类型的第一个泛型参数是否为
Bar
(这应该是完全匹配)
但是不知道怎么实现5and/or6.
(使用 java 11)
就像您对 Map
所做的那样,Set
很可能是 ParameterizedType
的一个实例,因此您可以检查它然后再进行转换。
从那个 ParameterizedType
(代表 Set<Something>
),你可以使用 getRawType
得到一个 Set
。那也是一个 Type
—— 但它应该可以安全地转换为 Class
。以防万一,您可以使用回退并使用其名称加载 class。
final Type valueType = mapType.getActualTypeArguments()[1];
final ParameterizedType pValueType = (ParameterizedType) valueType;
final Class<?> valueClass = pValueType.getRawType() instanceof Class ?
((Class<?>) pValueType.getRawType()) :
// Should be castable, but just in case it's not, fallback
getClass().getClassLoader().loadClass(
pValueType.getRawType().getTypeName()
);
if (Set.class.isAssignableFrom(valueClass)) { /* do something*/ }
这是完整的 runnable example。
class Foo {}
class Bar {}
interface MyValidMap extends Map<Foo, Set<Bar>> {}
public class Application {
public final HashMap<Foo, HashSet<Bar>> good = null;
public final Map<Foo, Set<Bar>> better = null;
public final String notAMap = null;
public final Map<String, Set<Bar>> badKey = null;
public final Map<Foo, List<String>> badValue = null;
public final Map<Foo, Set<String>> badSetElems = null;
public final MyValidMap noTypeParamMap = null;
public static void main(String[] args) throws Exception {
for (Field field : Application.class.getFields()) {
System.out.println(field.getName() + " - " + fieldMatches(field));
}
}
private static String fieldMatches(Field mapField) throws Exception {
if (!Map.class.isAssignableFrom(mapField.getType())) {
return "Field is not a Map";
}
if (!(mapField.getGenericType() instanceof ParameterizedType)) {
// We know it's a map, but it doesn't have type params. Probably something
// like this: class MyStringMap implements Map<String, String>. You can do
// something with getGenericInterfaces() but this seems so unlikely that
// it could just be ignored.
return "TODO";
}
final ParameterizedType mapType = (ParameterizedType) mapField.getGenericType();
final Type keyType = mapType.getActualTypeArguments()[0];
final Type valueType = mapType.getActualTypeArguments()[1];
if (Foo.class != keyType) {
return "Map's key type is not Foo";
}
if (!(valueType instanceof ParameterizedType)) {
// Same problem as above. May be a Set without type params
return "Map's value is (probably) not a Set";
}
final ParameterizedType pValueType = (ParameterizedType) valueType;
final Class<?> valueClass = pValueType.getRawType() instanceof Class ?
((Class<?>) pValueType.getRawType()) :
Application.class.getClassLoader().loadClass(
pValueType.getRawType().getTypeName()
);
if (!Set.class.isAssignableFrom(valueClass)) {
return "Map's value is not a Set";
}
final Type setElemType = pValueType.getActualTypeArguments()[0];
if (setElemType != Bar.class) {
return "Set's elements are not Bars";
}
return "Looks good";
}
}
输出:
good - Looks good
better - Looks good
notAMap - Field is not a Map
badKey - Map's key type is not Foo
badValue - Map's value is (probably) not a Set
badSetElems - Set's elements are not Bars
noTypeParamMap - TODO