在运行时使用给定类型创建一个通用映射

3
标题可能有点难理解,但是让我简要描述一下我的问题。
假设我有这样一个注释:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Identifier {

}

现在,我创建了一个类,并使用注释来注释其中的任何字段:
public class Student {
    private String name;
    private String surname;
    @Identifier
    private String idNumber;
    ...
}

最后,在运行时,我想创建一个键类型为 @Identifier 注释字段的类型和值类型为 StudentMap。请注意,任何字段都可以带有 @Identifier 注释。

有什么想法吗?

编辑

好的,让我澄清一下:

class Student {
    private String name;
    private String surname;
    @Identifier
    private String idNumber;
}

class Foo {
    @Identifier
    private Integer x;
}

//  Now, what I want to have are two maps:

SortedMap students;     //  key type: String
                        //  value type: Student
SortedMap foos;         //  key type: Integer
                        //  value type: Foo

提前感谢您!


2
您打算如何使用生成的“Map”? - Sotirios Delimanolis
好的,这是你无法做到的。keyType只能在运行时确定。valType也是如此。你可以创建Map,但你不能像那样有用地声明一个变量来保存它。 - Sotirios Delimanolis
泛型在运行时被擦除,所以根据我目前的理解,答案是“不,你不能这样做”。如果您在调用createTreeMap时知道类并可以为KV提供Class对象,则可以使用未经检查的转换来实现。否则,也许您可以澄清您真正想要在这里做什么。您真的需要泛型吗? - Radiodef
1
好的,我会建议按照答案所说的去做。如果您无法将类对象传递到方法调用中,则无法以通用方式执行此操作并使其类型安全(类型安全是指类对象将允许检查和快速失败转换)。 - Radiodef
1
@Pateman,两个答案都展示了基本的过程。for(/* 所有字段 */) { if(/* 字段被注解 */) { /* 将内容放入 map */ } } 你只需要返回一个 Map<Object, Object>Map<?, ?> 或者 Map(不推荐使用原始 Map),或者像这样一个 Map<? extends Comparable<?>, ? extends Serializable> - Radiodef
显示剩余11条评论
2个回答

1

我仍然不确定您想要做什么。

运行时我想创建一个 Map,其中键类型为 typeof(field annotated with @Identifier),值类型为 Student。

您可以创建原始的 MapMap<Object, Object>。您可以获取使用 @Identifier 注释的字段的类型。我不确定您所说的 value type of Student 是什么意思,因此我会假设您指的是类型 Student,即其 Class 对象。

public static void main(String[] args) throws Exception {
    Class<?> clazz = Student.class;
    Map<Object, Object> map = new HashMap<>();
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        Identifier annotation = field.getAnnotation(Identifier.class);
        if (annotation != null) {
            map.put(field.getType(), clazz);
        }
    }
    System.out.println(map);
}

使用您在问题中提供的示例类,这将打印。
{class java.lang.String=class com.spring.Student}

因此,已注释的字段类型被映射到类类型。

你无法拥有一个 Map<String,Student>,因为在编译时你不知道类型 String(可能甚至不知道 Student)。你可以尝试进行强制类型转换,但这会导致很多 ClassCastException


@Pateman 我不确定我的当前解决方案是否能够满足你的需求。 - Sotirios Delimanolis
不,它并不是,但是你回答的最后一部分表明我正在寻找的是不可能的。 - Pateman

0

所以你将要有一个方法(在我的例子中是myMethod),它将传递可能包含带有@Identifier注释的字段的对象。

很抱歉打破你的幻想,但是没有办法在运行时保留通用信息。你能做到的最接近的方式是拥有一个Map<Field, Class<?>>,它保存了你所需类型的键值对。这是你的做法:

public Map<Field, Class<?>> myMethod(Object obj) {
    Map<Field, Class<?>> result = new HashMap<Field, Class<?>>();
    for(Field field : obj.getClass().getDeclaredFields()) {
        Identifier identifier = field.getAnnotation(Identifier.class);
        if(identifier != null) {
            result.put(field, obj.getClass());
            return result;
        }
    }
    return result;
}

在我的例子中,结果要么是一个空的Map,要么是一个具有一个键值对的Map。我建议您应该使用一个单独的类型来代替Map作为结果。当然,如果您想要其他类型而不是Field,例如您可以使用FieldgetType()getGenericType()方法,那么您可以修改代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接