You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

注解处理器无法获取类型变量的@Nullable注解问题排查与解决

问题描述

给定如下Java方法:

public static <T, @Nullable R> void foo(@Nullable Object obj, T t, @Nullable List<? extends @Nullable R> r)

通过Gradle配置的annotationProcessor可以读取@Nullable Object obj中的@Nullable注解,但无法读取类型变量R(包括<T, @Nullable R>和List<? extends @Nullable R>中的R)的@Nullable注解。

相关注解处理器代码片段如下:

List<? extends TypeParameterElement> typeParameterElements = element.getTypeParameters();
if (typeParameterElements != null && !typeParameterElements.isEmpty()) {
    methodInfo.setTypeParams(typeParameterElements.stream().map(it -> buildTypeName(it.asType())).collect(Collectors.toList()));
}

private String buildTypeName(TypeMirror typeMirror) {
    String annotations = "";
    // 这里无法获取类型变量R的@Nullable注解!
    List<? extends AnnotationMirror> annotationMirrors = typeMirror.getAnnotationMirrors();
    if (!annotationMirrors.isEmpty()) {
        annotations = annotationMirrors.stream().map(i -> i.toString() + " ").collect(Collectors.joining());
    }
    if (typeMirror instanceof DeclaredType) {
        DeclaredType declaredType = (DeclaredType) typeMirror;
        List<? extends TypeMirror> args = declaredType.getTypeArguments();
        if (args.isEmpty()) {
            return annotations + declaredType.asElement().toString();
        }
        String argsNames = args.stream().map(this::buildTypeName).collect(Collectors.joining(", "));
        return annotations + declaredType.asElement().toString() + "<" + argsNames + ">";
    }
    // 永远不会走到WildcardType分支,为什么?
    if (typeMirror instanceof WildcardType) {
        WildcardType wildcardType = (WildcardType) typeMirror;
        TypeMirror up = wildcardType.getSuperBound();
        if (up != null) {
            return "? super " + buildTypeName(up);
        }
        TypeMirror sp = wildcardType.getExtendsBound();
        if (sp != null) {
            return "? extends " + buildTypeName(sp);
        }
    }
    if (typeMirror instanceof IntersectionType) {
        IntersectionType intersectionType = (IntersectionType) typeMirror;
        List<? extends TypeMirror> bounds = intersectionType.getBounds();
        String boundNames = bounds.stream().map(this::buildTypeName).collect(Collectors.joining(" & "));
        return annotations + typeUtils.asElement(intersectionType) + " extends " + boundNames;
    }
    if (typeMirror instanceof TypeVariable) {
        TypeVariable typeVariable = (TypeVariable) typeMirror;
        TypeMirror lower = typeVariable.getLowerBound();
        if (lower != null && !(lower instanceof NullType)) {
            return annotations + typeMirror + " super " + buildTypeName(lower);
        }
        TypeMirror upper = typeVariable.getUpperBound();
        if (upper != null && !(upper instanceof NullType) && !Objects.equals("java.lang.Object", upper.toString())) {
            return annotations + typeMirror + " extends " + buildTypeName(upper);
        }
    }
    return annotations + typeMirror;
}
问题原因
  1. 类型变量注解的读取位置错误:类型变量(比如<T, @Nullable R>中的R)的@Nullable注解是附着在TypeParameterElement实例上的,而不是它对应的TypeMirror上。当前代码通过it.asType()将TypeParameterElement转为TypeMirror后调用getAnnotationMirrors(),自然无法获取到注解。
  2. 通配符边界的注解处理缺失:对于List<? extends @Nullable R>中的通配符类型,虽然代码里有WildcardType分支,但当前逻辑没有正确处理通配符边界上的注解;另外,如果通配符边界是类型变量,也需要确保能读取到该变量本身的注解。
解决方案

1. 修正类型变量注解的读取逻辑

直接从TypeParameterElement获取注解,再结合其TypeMirror的信息构建类型名称:

List<? extends TypeParameterElement> typeParameterElements = element.getTypeParameters();
if (typeParameterElements != null && !typeParameterElements.isEmpty()) {
    methodInfo.setTypeParams(typeParameterElements.stream()
            .map(this::buildTypeParamName) // 改用专门处理TypeParameterElement的方法
            .collect(Collectors.toList()));
}

// 新增处理TypeParameterElement的方法
private String buildTypeParamName(TypeParameterElement paramElement) {
    // 直接从TypeParameterElement获取注解
    String annotations = paramElement.getAnnotationMirrors().stream()
            .map(AnnotationMirror::toString)
            .collect(Collectors.joining(" "));
    // 结合TypeMirror的边界信息构建完整名称
    return annotations + buildTypeName(paramElement.asType());
}

2. 完善通配符类型的处理逻辑

确保WildcardType分支能正确触发,并递归处理边界类型的注解:

if (typeMirror instanceof WildcardType) {
    WildcardType wildcardType = (WildcardType) typeMirror;
    TypeMirror superBound = wildcardType.getSuperBound();
    if (superBound != null) {
        return "? super " + buildTypeName(superBound);
    }
    TypeMirror extendsBound = wildcardType.getExtendsBound();
    if (extendsBound != null) {
        // 递归处理边界类型,自动继承类型变量的注解逻辑
        return "? extends " + buildTypeName(extendsBound);
    }
    // 处理无边界的通配符
    return "?";
}

3. 优化TypeVariable分支的返回逻辑

确保类型变量在无边界时,能正确返回带注解的名称:

if (typeMirror instanceof TypeVariable) {
    TypeVariable typeVariable = (TypeVariable) typeMirror;
    TypeMirror lowerBound = typeVariable.getLowerBound();
    if (lowerBound != null && !(lowerBound instanceof NullType)) {
        return annotations + typeVariable + " super " + buildTypeName(lowerBound);
    }
    TypeMirror upperBound = typeVariable.getUpperBound();
    if (upperBound != null && !(upperBound instanceof NullType) && !Objects.equals("java.lang.Object", upperBound.toString())) {
        return annotations + typeVariable + " extends " + buildTypeName(upperBound);
    }
    // 无边界时直接返回带注解的类型变量名
    return annotations + typeVariable;
}

内容的提问来源于stack exchange,提问作者FredSuvn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 06:30:11