Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

kotlin wrong nullability inference without any generics

in spring mvc (5.1.3) i'm trying to do:

val url : String? = null
val matcher: ResultMatcher = MockMvcResultMatchers.forwardedUrl(url)

and i get compilation error for the second line.

from intellij (kotlinc-jvm 1.3.11):

Error:(230, 56) Kotlin: Null can not be a value of a non-null type String

or from gradle (kotlin 1.2.71):

 Type mismatch: inferred type is String? but String was expected

the java source code of spring method is:

/**
 * Asserts the request was forwarded to the given URL.
 * <p>This method accepts only exact matches.
 * @param expectedUrl the exact URL expected
 */
public static ResultMatcher forwardedUrl(String expectedUrl) {
    return result -> assertEquals("Forwarded URL", expectedUrl, result.getResponse().getForwardedUrl());
}

intellij displays javadoc:

org.springframework.test.web.servlet.result.MockMvcResultMatchers @NotNull 
@Contract(pure = true) 
public static org.springframework.test.web.servlet.ResultMatcher forwardedUrl(@Nullable String expectedUrl)

so why the compiler still requires non-nullable type and how to bypass that requirement?

like image 879
piotrek Avatar asked Dec 23 '18 22:12

piotrek


1 Answers

There are no explicit nullability annotations on the parameter of MockMvcResultMatchers.forwardedUrl, so it will default to @NonNullApi, as indicated in the package-info.java:

/**
 * Contains built-in {@code ResultMatcher} and {@code ResultHandler} implementations.
 * <p>Use {@link org.springframework.test.web.servlet.result.MockMvcResultMatchers}
 * and {@link org.springframework.test.web.servlet.result.MockMvcResultHandlers}
 * to access instances of those implementations.
 */
@NonNullApi
@NonNullFields
package org.springframework.test.web.servlet.result;

import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

There's no way to describe nullability in Java, so by default passing both String and String? into a Java method that accepts a String would work. However, Kotlin compiler honors different flavors of nullability annotations (such as javax.annotation.*, Android's annotations, Spring's annotations) to allow Java code to provide nullability information to Kotlin callers.

How to bypass this? You can't. You're passing an argument of the wrong type - String and String? are different types in Kotlin.

like image 185
Egor Avatar answered Nov 03 '22 00:11

Egor