kotlin 注释处理器中的可空类型

Posted

技术标签:

【中文标题】kotlin 注释处理器中的可空类型【英文标题】:Nullable types in kotlin annotation processor 【发布时间】:2018-01-28 06:31:55 【问题描述】:

我正在为 Kotlin 开发注解处理器,因为处理的元素是在 Java 中,所以我没有收到可以为 ? 的可空值,而是使用 @Nullable 注解,这很好,但我在接收时遇到了问题对于普通参数,类型和高阶函数中的 null 参数。

var someNullField: String? = "" 

我将在过程中收到java.lang.String,并在其注释中添加@org.jetbrains.annotations.Nullable

但是 List<String?> 例如会返回我 java.util.List<java.lang.String> 没有任何注释,不在主元素中,不在类型参数中,导致未知的可空性状态

我尝试使用javax.lang.model.util.Types 找到某种结果,但什么也没有。

我现在使用的一些代码:

val utils = processingEnvironment.typeUtils
val type = fieldElement.asType()
if (type is DeclaredType) 
    val typeElement = utils.asElement(type)
    type.typeArguments
            .forEach 
                //Trying different ways and just printing for possible results
                val capture = utils.capture(it)
                val erasure = utils.erasure(it)
                val element = utils.asElement(it)
                printMessage("element: $element isNullable: $element.isNullable() isNotNull: $element.isNotNull()\ncapture: $capture isNullable: $capture.isNullable() isNotNull: $capture.isNotNull()\nerasure: $erasure isNullable: $erasure.isNullable() isNotNull: $erasure.isNotNull()")
            

我们将不胜感激。

【问题讨论】:

【参考方案1】:

一些必要的历史:从 Java 6 开始(当 Mirror API 公开时)Java 注释不能用于任何东西,但相同类型的***元素可以通过反射访问。您可以注释类、方法和字段,但不能注释类型参数 (List<String>) 或局部变量 (String value = ...)。 Sun/Oracle 工程师已经承认了这个限制,并且在 Java 8 中诞生了所谓的“类型注释”。

类型注解可以针对任何typetype局部变量,数组组件类型,类型变量type 甚至是返回类型(后面的注解也是类似的,但与方法上的老式注解不同!)。类型注释是通过新的@Target 值创建的:ElementType#TYPE_USE。

当 Kotlin 人写作时

List<String?>

真的是这样

List<@Nullable String>

可以读作:“可空字符串元素列表”。

由于 type 本身就是目标,因此您应该通过检查它的原始 TypeMirror 来获取注释(不要打扰已擦除或捕获的 TypeMirror,它们没有足够的连接到源代码以保留注释)。巧合的是,对 Mirror API 进行了重构,产生了新的接口AnnotatedConstruct,方便地把 TypeMirror 做成了它的后代。


现在有个坏消息:到 Java 8 发布时,对检查类型注释的支持显然还没有为生产做好准备,所以它被淘汰了。 JSR 已被重写以暗示“TypeMirror#getAnnotationMirrors”应该不返回任何内容。

从公共 API 中删除的部分支持仍然可以通过 Oracle 的供应商特定的Tree API 获得(仅在 javac 中支持)。由 Tree#getTypeMirror 返回的 TypeMirror 可能包含您期望的注释。但是由于它是错误的,您只能通过一系列 hack 获得注释,最终,这不会在任何时候都有效(例如在嵌套类型参数的情况下)。有关该方向的一些研究,请参阅 this question。

The fix 因为这个烂摊子被合并到 Java 9 中。我还没有测试它,但看起来 TypeMirror#getAnnotationMirrors 最终可能会起作用。没有计划将修复程序向后移植到较旧的 Java 版本。

【讨论】:

感谢详细描述的答案,我想出了某种解决方案/破解方法:基本上创建一个名为 NullableOf 的泛型类,这样我就总能知道它何时为 null 。有没有更简单的方法来做到这一点,或者只是创建一个新类 Java 8 中有一个类,称为“可选”。但这很糟糕:使用Collection&lt;Optional&lt;*&gt;&gt; 时会创建大量样板文件,创建大量不必要的对象。如果您主要关心集合(90% 的泛型用例),您可以通过不支持 null 的专用集合类型传达可空性(野外已经有多个这样的集合)。此外,如果您真的关心该用例,您可以通过 Tree API 自行破解对类型镜像自省的支持。这是可能的(但需要解析源代码树)。 好的,谢谢,我可能会先尝试树 API,如果它不起作用,我会检查我将使用什么,因为可选的是 Java 8,我需要对 Java 7 的支持 @GilGoldzweig 注意,如果你走这条路,你的用户将不得不使用 Java 8 javac 构建项目(因为旧版本的编译器不支持类型注释)。

以上是关于kotlin 注释处理器中的可空类型的主要内容,如果未能解决你的问题,请参考以下文章

#5 kotlin nullable 可空类型

Kotlin系列之可空类型的处理

C# 8中的可空引用类型

c# 中的可空类型是啥?

PHP7 中的可空返回类型

Kotlin空安全 ① ( Kotlin 的空安全机制 | 变量可空性 | 默认变量不可赋空值 | 声明可空类型变量 )