我试图编写一个实用程序方法,它允许我获取字段的定义,包括所有泛型参数。为此,我通过Field.getGenericType()
检索字段的泛型类型,并解析具有以下语法的类型名称(在EBNF中):
generictype = classname [ '<' generictype { ',' ' ' generictype } '>' ]
classname = package '.' ( letter | '_' ) { letter | digit | '_' }
package = ( packagepart { '.' packagepart } ) |
packagepart = ( letter | '_' ) { letter | digit | '_' }
我首次尝试解析这个问题是使用正则表达式
(?<argument>\w+(?:\.\w+)*(?:\<(?<arglist>\g<argument>(?:,\s\g<argument>)*)\>)?)
可以检查其详细信息here。这个正则表达式正是我所需要的。现在,Java正则表达式不支持\g<name>
构造,所以如果我想支持参数中未知深度的泛型类型,我就不能使用这种方法。
我可以使用其他方法吗?如果是的话,我怎样才能实现我想做的事情?
编辑:我想要实现此目的的原因是因为我有一个配置并希望将其内容传输到对象的相应字段中。某种反序列化,如果你想以这种方式调用它。现在,配置仅支持原始类型java.lang.String
,java.util.List<T>
,java.util.Map<K, V>
和java.util.Map.Entry<K, V>
。要检索这些类的值,客户端必须提供一个类作为参数,该参数将用于反序列化配置中保存的字符串。因此,我必须确定一个类的字段使用哪个泛型参数以及它们对应的Class
。
答案 0 :(得分:1)
您可以执行以下操作:
Type type = Field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Class<?> genericType = (Class<?>) pt.getActualTypeArguments()[0];
}
如果这还不够,只需使用Google反射库:https://github.com/google/guava/wiki/ReflectionExplained
答案 1 :(得分:1)
如果你真的需要解析(高阶段的方法看起来更优雅,但解析是问题所要求的),我会用递归下降解析来做这个,如下所示:
class GenericType {
String baseName;
List<GenericType> params;
GenericType(String baseName, List<GenericType> params) {
this.baseName = baseName;
this.params = params;
}
static GenericType parse(String s) {
StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(s));
tokenizer.wordChars('.', '.'); // Make dots part of the name
try {
tokenizer.nextToken(); // Skip "BOF" token
return parse(tokenizer);
} catch (IOException e) {
throw new RuntimeException();
}
}
static GenericType parse(StreamTokenizer tokenizer) throws IOException {
String baseName = tokenizer.sval;
tokenizer.nextToken();
List<GenericType> params = new ArrayList<>();
if (tokenizer.ttype == '<') {
do {
tokenizer.nextToken(); // Skip '<' or ','
params.add(parse(tokenizer));
} while (tokenizer.ttype == ',');
tokenizer.nextToken(); // skip '>'
}
return new GenericType(baseName, params);
}
}
答案 2 :(得分:0)
我找到了一个解决方案here,在一个试图完全相同的问题上,唯一的区别是它在C#中。由于这种差异,我不得不稍微重写代码Erik_at_Digit,最终得到了这个解决方案:
public class ClassUtil {
// https://stackoverflow.com/questions/20532691/how-to-parse-c-sharp-generic-type-names?rq=1
static List<String> splitByComma(String typeArgumentList) {
List<String> strings = new LinkedList<>();
StringBuilder sb = new StringBuilder();
int level = 0;
for (int i = 0; i < typeArgumentList.length(); i++) {
char c = typeArgumentList.charAt(i);
if (c == ',' && level == 0) {
strings.add(sb.toString());
sb.setLength(0);
} else {
sb.append(c);
}
if (c == '<') {
level++;
}
if (c == '>') {
level--;
}
}
strings.add(sb.toString());
return strings;
}
static GenericType getGenericType(String description) throws ClassNotFoundException {
Type type;
GenericType[] parameters;
if (!description.contains("<")) {
type = Class.forName(description);
parameters = new GenericType[0];
} else {
int start = description.indexOf('<');
int end = description.lastIndexOf('>');
String typeArgumentList = description.substring(start + 1, end);
String name = description.substring(0, start);
List<String> arguments = splitByComma(typeArgumentList);
type = Class.forName(name);
parameters = new GenericType[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
String argument = arguments.get(i).trim();
parameters[i] = getGenericType(argument);
}
}
return new GenericType(type, parameters);
}
public static GenericType getGenericType(Type type) throws ClassNotFoundException {
String description = type.getTypeName();
if (!description.contains("<")) return new GenericType(type);
return getGenericType(description);
}
static List<Map<List<String>, Object>> field = new LinkedList<>();
public static void main(String[] args) throws Throwable {
Field field = ClassUtil.class.getDeclaredField("field");
System.out.println(field.getGenericType());
System.out.println(getGenericType(field.getGenericType()));
}
static class GenericType implements Type {
final String typeName;
final Type type;
final GenericType[] parameters;
public GenericType(Type type, GenericType... parameters) {
this.type = type;
this.parameters = parameters;
typeName = buildTypeName(type, parameters);
}
private static String buildTypeName(Type type, GenericType... parameters) {
StringBuilder s = new StringBuilder();
s.append(type.getTypeName());
if (parameters.length > 0) {
CharSequence[] names = new CharSequence[parameters.length];
for (int i = 0; i < parameters.length; i++) {
names[i] = parameters[i].getTypeName();
}
s.append("<").append(String.join(", ", names)).append(">");
}
return s.toString();
}
@Override
public String toString() {
return getTypeName();
}
@Override
public String getTypeName() {
return typeName;
}
}
}
这肯定不是具有最佳性能的解决方案,但它完成了它的设计目标。