Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nullable types in kotlin annotation processor

I'm working on annotation processor for Kotlin and because the processed elements are in Java I don't receive nullables as ? instead with a @Nullable annotation and that's fine, but I'm facing a problem with receiving null parameters in types and in higher order functions, for normal parameters.

var someNullField: String? = "" 

I will receive java.lang.String at process with @org.jetbrains.annotations.Nullable in its annotations.

But List<String?> for example will return me java.util.List<java.lang.String> without any annotations not in the main element not in the type arguments which results in a unknown nullability state

I tried using javax.lang.model.util.Types to find some sort of result but nothing.

Some of the code that i'm using now:

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()}")
            }
}

All help will be appreciated.

like image 362
Gil Goldzweig Avatar asked Aug 19 '17 20:08

Gil Goldzweig


People also ask

What are nullable types in the Kotlin language?

In Kotlin, there's a distinction between nullable and non-nullable types: Nullable types are variables that can hold null . Non-null types are variables that can't hold null .

What does nullable annotation mean?

public annotation Nullable. Denotes that a parameter, field or method return value can be null. When decorating a method call parameter, this denotes that the parameter can legitimately be null and the method will gracefully deal with it. Typically used on optional parameters.

Is any nullable Kotlin?

Any is only a superclass of non-nullable types. This makes it possible to write code that requires a non-null instance of anything, and still benefit from the safety of the type-checker (unlike when using Java's Object ).

How do you handle null in Kotlin?

You can use the "?. let" operator in Kotlin to check if the value of a variable is NULL. It can only be used when we are sure that we are refereeing to a non-NULL able value. The following example demonstrates how this operator works.


1 Answers

A bit of necessary history: as of Java 6 (when the Mirror API was made public) Java annotations could not be used on anything, but the same kinds of top-level elements, accessible via reflection. You could annotate classes, methods and fields, but could not annotate type arguments (List<String>) or local variables (String value = ...). Sun/Oracle engineers have acknowledged that limitation, and in Java 8 the so-called "type annotations" were born.

Type annotations can target type of anything: type of local variable, array component type, type variable type and even return type (later annotation is placed similarly, but is distinct from the old-school annotations on the method!). Type annotations are created via new @Target value: ElementType#TYPE_USE.

When Kotlin people write

List<String?>

That really means

List<@Nullable String>

which can be read as: "the list of nullable String elements".

Since the type itself is being targeted, you are expected to obtain annotations by examining it's original TypeMirror (don't bother with erased or captured TypeMirrors, they don't have enough connection to source code to retain the annotations). Coincidentally, the Mirror API was refactored, resulting in the new interface AnnotatedConstruct, and conveniently making TypeMirror it's descendant.


Now the bad news: by the time of Java 8 release the support for inspecting type annotations apparently wasn't production-ready, so it got butchered. The JSR has been rewritten to imply, that "TypeMirror#getAnnotationMirrors" is supposed to return nothing.

The bits of support, that were removed from public API, are still available via Oracle's vendor-specific Tree API (supported in javac only). The TypeMirror, returned by Tree#getTypeMirror may contain the annotation the way you are expecting it to. But since it is buggy, you will only be able to get annotations via series of hacks, and ultimately, this won't work at all times (such as in case of nested type arguments). See this question for some research in that direction.

The fix for that mess was merged in Java 9. I haven't tested it yet, but it looks like TypeMirror#getAnnotationMirrors might finally work. There are no plans to backport the fix to the older Java version.

like image 97
user1643723 Avatar answered Oct 21 '22 23:10

user1643723