Java 绑定到具有特定注释的 类 的通用类型
Java generic type bounded to classes with specific annotation
在 java 中可以将通用参数绑定到 classes 实现特定接口,所以下面是可能的
interface MyInterface {}
class MyClassA implements MyInterface {}
class MyBoundedClassA<T extends MyInterface>
现在,如果我想将参数绑定到带有特定注释的 class 接口会怎样,例如:
interface @MyAnnotation {}
@MyAnnotation
class MyClassB {}
class MyBoundedClassB<T extends MyAnnotation> // NOT possible
是否可以在 Java 中实现这样的行为?
---- 编辑
根据要求添加现实世界的例子。稍微修改域以使示例更易于理解。
有一个众所周知的用于序列化对象的 jackson 库。此库不支持除字符串以外的映射键的序列化,因此开箱即用是不可能的
class TimeRange {
LocalDateTime startDate;
LocalDateTime endDate;
}
class SportsmenActivities {
private Map<String, <TimeRange, List<Activity>> sportActivities;
}
在此示例中,外图的键是"sportsmanCode" liek "andy"、"mike"、"john"。内图包含给定运动员在给定时间段内进行的活动。
比方说 Andy 慢跑了一天,条目应该是:
new SportsmanActivities().get("andy").put(TimeRange.of('2012-12-01,'2012-12-02'), List.with(new JoggingActivity)) // did some pseudo code here for readablity
Jackson 不会直接序列化它,所以我编写了通用模块来序列化这种复杂的地图。
要使用它,您需要做的是像这样注释您的 "key" class:
@KeySerializable
class TimeRange {
@MyMapKey
LocalDateTime startDate;
@MyMapKey
LocalDateTime endDate;
}
您可以猜到用@MyMapKey 注释的字段将用于生成 MapKey。
现在我有一个 jackson class 的实现,它动态序列化作为 "text map key" 传递的所有内容,并用 @KeySerializable 注释。签名如下
class MyMapKeySerializer<T> extends JsonSerializer<T> {
serialize (T keyToSerialize) {
// do magic
}
}
这行得通,但我想将 T 限制为仅接受使用 @KeySerializable 注释的 classes,因为仅对于此类 classes 此方法才有意义。理想情况下,这应该是这样的:
class MyMapKeySerializer<T annotatedWith @KeySerializable> extends JsonSerializer<T> {
serialize (T keyToSerialize) {
// do magic
}
}
如果您的目标是断言只有带注释的 类 被接受,那么您几乎没有解决方法:
- 编写一个在编译时执行断言的注释处理器(查看@NonNull 等如何工作)。这是一项有趣的工作,但并不平凡,因为 compilation/type 系统对许多经验丰富的 Java 开发人员来说是全新的。
- 使用某种形式的 AOP(AspectJ、Spring AOP 等)"advise" 所有带有装饰器的注释方法,装饰器的职责是断言参数具有相同的注释。
- 在运行时使用
parameter.getClass().isAnnotationPresent(MyAnnotation.class)
明确检查
像 Checker Framework 这样的工具可以插入编译器,以类似于您所请求的方式限制泛型实例化。它作为注释处理器实现,并提供正确使用的编译时保证。
For example,可以写成class MyList<T extends @NonNull Object> {...}
.
Checker Framework 使您能够 build your own checker, which enforces any rules you like about @KeySerializable
. In your case, the rules might be so simple that you can just define a couple of type qualifiers and use the Subtyping Checker -- 至少在一开始是这样。
请注意,要使 Checker Framework 使用 @KeySerializable
注释工作,该注释必须是类型注释而不是声明注释。
在 java 中可以将通用参数绑定到 classes 实现特定接口,所以下面是可能的
interface MyInterface {}
class MyClassA implements MyInterface {}
class MyBoundedClassA<T extends MyInterface>
现在,如果我想将参数绑定到带有特定注释的 class 接口会怎样,例如:
interface @MyAnnotation {}
@MyAnnotation
class MyClassB {}
class MyBoundedClassB<T extends MyAnnotation> // NOT possible
是否可以在 Java 中实现这样的行为?
---- 编辑
根据要求添加现实世界的例子。稍微修改域以使示例更易于理解。
有一个众所周知的用于序列化对象的 jackson 库。此库不支持除字符串以外的映射键的序列化,因此开箱即用是不可能的
class TimeRange {
LocalDateTime startDate;
LocalDateTime endDate;
}
class SportsmenActivities {
private Map<String, <TimeRange, List<Activity>> sportActivities;
}
在此示例中,外图的键是"sportsmanCode" liek "andy"、"mike"、"john"。内图包含给定运动员在给定时间段内进行的活动。
比方说 Andy 慢跑了一天,条目应该是:
new SportsmanActivities().get("andy").put(TimeRange.of('2012-12-01,'2012-12-02'), List.with(new JoggingActivity)) // did some pseudo code here for readablity
Jackson 不会直接序列化它,所以我编写了通用模块来序列化这种复杂的地图。
要使用它,您需要做的是像这样注释您的 "key" class:
@KeySerializable
class TimeRange {
@MyMapKey
LocalDateTime startDate;
@MyMapKey
LocalDateTime endDate;
}
您可以猜到用@MyMapKey 注释的字段将用于生成 MapKey。
现在我有一个 jackson class 的实现,它动态序列化作为 "text map key" 传递的所有内容,并用 @KeySerializable 注释。签名如下
class MyMapKeySerializer<T> extends JsonSerializer<T> {
serialize (T keyToSerialize) {
// do magic
}
}
这行得通,但我想将 T 限制为仅接受使用 @KeySerializable 注释的 classes,因为仅对于此类 classes 此方法才有意义。理想情况下,这应该是这样的:
class MyMapKeySerializer<T annotatedWith @KeySerializable> extends JsonSerializer<T> {
serialize (T keyToSerialize) {
// do magic
}
}
如果您的目标是断言只有带注释的 类 被接受,那么您几乎没有解决方法:
- 编写一个在编译时执行断言的注释处理器(查看@NonNull 等如何工作)。这是一项有趣的工作,但并不平凡,因为 compilation/type 系统对许多经验丰富的 Java 开发人员来说是全新的。
- 使用某种形式的 AOP(AspectJ、Spring AOP 等)"advise" 所有带有装饰器的注释方法,装饰器的职责是断言参数具有相同的注释。
- 在运行时使用
parameter.getClass().isAnnotationPresent(MyAnnotation.class)
明确检查
像 Checker Framework 这样的工具可以插入编译器,以类似于您所请求的方式限制泛型实例化。它作为注释处理器实现,并提供正确使用的编译时保证。
For example,可以写成class MyList<T extends @NonNull Object> {...}
.
Checker Framework 使您能够 build your own checker, which enforces any rules you like about @KeySerializable
. In your case, the rules might be so simple that you can just define a couple of type qualifiers and use the Subtyping Checker -- 至少在一开始是这样。
请注意,要使 Checker Framework 使用 @KeySerializable
注释工作,该注释必须是类型注释而不是声明注释。