如何在 JavaPoet 中添加一个参数化的超级接口?
How to add a parameterized super interface in JavaPoet?
我正在编写一个生成 JSON 序列化代码的注解处理器。这是我用来识别需要序列化程序
的 POJO
的注释
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface JsonSerialize {
}
这是我的序列化程序的基本接口
public interface JsonSerializer<T> {
String serialize(T t);
}
这是查找该注释并生成序列化程序代码的注释处理器代码
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(JsonSerialize.class)) {
if (element.getKind() == ElementKind.CLASS) {
MethodSpec serializeMethod = MethodSpec
.methodBuilder("serialize")
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeName.get(element.asType()), "obj", Modifier.FINAL).build())
.returns(String.class)
.addStatement("return \"dummy string\"")
.build();
TypeSpec serializer = TypeSpec
.classBuilder(element.getSimpleName().toString() + "JsonSerializer")
.addSuperinterface(JsonSerializer.class) // THIS LINE IS WRONG
.addModifiers(Modifier.PUBLIC)
.addMethod(serializeMethod)
.build();
try {
JavaFile.builder(processingEnv.getElementUtils().getPackageOf(element).toString(), serializer)
.build()
.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
但是我得到一个编译错误,因为我生成的 class 没有在它的继承中指定通用参数。我该如何指定?
您无需将 java.lang.Class
传递给 addSuperinterface
方法,而是需要传递一些包含您所考虑的特定类型详细信息的内容。此方法有两个重载 - 一个采用 java.lang.reflect.Type
(Class
是此方法的子类型),另一个采用 com.squareup.javapoet.TypeName
)。从技术上讲,两者都可行,但由于您已经在使用 JavaPoet,我鼓励您尝试创建 TypeName 实例。
TypeName
有许多子classes,ClassName
、ParameterizedTypeName
可能是这里主要关注的。在注释处理器中,它们比使用 Class
实例有一些很大的优势——主要是你不需要真正能够加载或引用你正在谈论的 class——有点像您在代码的其他地方使用了 element.getSimpleName().toString()
。
这些 classes 有创建它们的静态方法,可以基于多种事物。我们在这里感兴趣的是:
/** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */
public static ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments)
在你的代码中,你会大致像这样使用它:
...
.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(JsonSerializer.class),//rawType
ClassName.get(whateverTShouldBe) //the value for T
))
...
很有可能 T
最终在这里也可以是通用的,例如 List<String>
,因此您应该注意正确构建传递到那里的类型 - 它本身可能是 ParameterizedTypeName
。为此,请密切注意 TypeName
中的各种方法 - 例如,TypeName.get(TypeMirror)
重载将采用已参数化的声明类型镜像,并再次返回预期的 ParameterizedTypeName
。
话虽如此,根据您的其他代码,T
今天不能是通用的 - 您在 Element
上寻找 @JsonSerialize
注释,这意味着它将是等效的List<T>
而不是它的用法,List<String>
。然后,在这一行中,您将 Element 变成 TypeMirror 以构建类型名称,如我上面所述:
.addParameter(ParameterSpec.builder(TypeName.get(element.asType()), "obj", Modifier.FINAL).build())
这意味着最终代码可能是
...
.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(JsonSerializer.class),//rawType
TypeName.get(element.asType()) //the value for T
))
...
我正在编写一个生成 JSON 序列化代码的注解处理器。这是我用来识别需要序列化程序
的POJO
的注释
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface JsonSerialize {
}
这是我的序列化程序的基本接口
public interface JsonSerializer<T> {
String serialize(T t);
}
这是查找该注释并生成序列化程序代码的注释处理器代码
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(JsonSerialize.class)) {
if (element.getKind() == ElementKind.CLASS) {
MethodSpec serializeMethod = MethodSpec
.methodBuilder("serialize")
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeName.get(element.asType()), "obj", Modifier.FINAL).build())
.returns(String.class)
.addStatement("return \"dummy string\"")
.build();
TypeSpec serializer = TypeSpec
.classBuilder(element.getSimpleName().toString() + "JsonSerializer")
.addSuperinterface(JsonSerializer.class) // THIS LINE IS WRONG
.addModifiers(Modifier.PUBLIC)
.addMethod(serializeMethod)
.build();
try {
JavaFile.builder(processingEnv.getElementUtils().getPackageOf(element).toString(), serializer)
.build()
.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
但是我得到一个编译错误,因为我生成的 class 没有在它的继承中指定通用参数。我该如何指定?
您无需将 java.lang.Class
传递给 addSuperinterface
方法,而是需要传递一些包含您所考虑的特定类型详细信息的内容。此方法有两个重载 - 一个采用 java.lang.reflect.Type
(Class
是此方法的子类型),另一个采用 com.squareup.javapoet.TypeName
)。从技术上讲,两者都可行,但由于您已经在使用 JavaPoet,我鼓励您尝试创建 TypeName 实例。
TypeName
有许多子classes,ClassName
、ParameterizedTypeName
可能是这里主要关注的。在注释处理器中,它们比使用 Class
实例有一些很大的优势——主要是你不需要真正能够加载或引用你正在谈论的 class——有点像您在代码的其他地方使用了 element.getSimpleName().toString()
。
这些 classes 有创建它们的静态方法,可以基于多种事物。我们在这里感兴趣的是:
/** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */
public static ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments)
在你的代码中,你会大致像这样使用它:
...
.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(JsonSerializer.class),//rawType
ClassName.get(whateverTShouldBe) //the value for T
))
...
很有可能 T
最终在这里也可以是通用的,例如 List<String>
,因此您应该注意正确构建传递到那里的类型 - 它本身可能是 ParameterizedTypeName
。为此,请密切注意 TypeName
中的各种方法 - 例如,TypeName.get(TypeMirror)
重载将采用已参数化的声明类型镜像,并再次返回预期的 ParameterizedTypeName
。
话虽如此,根据您的其他代码,T
今天不能是通用的 - 您在 Element
上寻找 @JsonSerialize
注释,这意味着它将是等效的List<T>
而不是它的用法,List<String>
。然后,在这一行中,您将 Element 变成 TypeMirror 以构建类型名称,如我上面所述:
.addParameter(ParameterSpec.builder(TypeName.get(element.asType()), "obj", Modifier.FINAL).build())
这意味着最终代码可能是
...
.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(JsonSerializer.class),//rawType
TypeName.get(element.asType()) //the value for T
))
...