Generics and inheritance in Java library used from Kotlin

Viewed 289

I'm having problem implementing a Kotlin class which uses Java-defined generics/interfaces classes from MockMvc library.

Library classes:

public interface ConfigurableMockMvcBuilder<B extends ConfigurableMockMvcBuilder<B>> extends MockMvcBuilder {

    // ...

    <T extends B> T defaultRequest(RequestBuilder requestBuilder);

    // ...

}

@FunctionalInterface
public interface MockMvcBuilderCustomizer {

    /**
     * Customize the given {@code builder}.
     * @param builder the builder
     */
    void customize(ConfigurableMockMvcBuilder<?> builder);

}

I would like to implement this (in Java) :

@Component
public class ApiKeyHeaderMockMvcBuilderCustomizer implements MockMvcBuilderCustomizer {

    @Override
    public void customize(ConfigurableMockMvcBuilder<?> builder) {
        RequestBuilder apiKeyRequestBuilder = MockMvcRequestBuilders.get("any")
            .header("api-key-header", "apikeyvalue");
        builder.defaultRequest(apiKeyRequestBuilder);
    }    
}

But when I try to rewrite the above class in Kotlin, I'm having troubles figuring out what generic constraint I should use both in customize's parameter and when calling builder.defaultRequest

@Component
class ApiKeyHeaderMockMvcBuilderCustomizer : MockMvcBuilderCustomizer {
    // is this generic right?
    override fun customize(builder: ConfigurableMockMvcBuilder<*>) { 
        val apiKeyRequestBuilder: RequestBuilder =
            MockMvcRequestBuilders.get("http://any")
                .header("api-key-header", "apikeyvalue")

        // won't compile, using Nothing compiles but fails at runtime
        builder.defaultRequest<*>(apiKeyRequestBuilder)
    }
}

Copy-pasting the code from Java in IntelliJ Idea converts to this Kotlin code :

@Component
class ApiKeyHeaderMockMvcBuilderCustomizer : MockMvcBuilderCustomizer {
    override fun customize(builder: ConfigurableMockMvcBuilder<*>) {
        val apiKeyRequestBuilder: RequestBuilder = MockMvcRequestBuilders.get("any")
            .header("api-key-header", "apikeyvalue")
        builder.defaultRequest(apiKeyRequestBuilder)
    }
}

With error under defaultRequest :

Type inference failed: Not enough information to infer parameter T in 
fun <T : Nothing!> defaultRequest(requestBuilder: RequestBuilder) : T
Please specify it explicitly.

When I try using Nothing or Nothing? in as generic parameter for defaultRequest, I get runtime NullPointerException in place of the method call. The implementation of the defaultRequest method is :

public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>>
        extends MockMvcBuilderSupport implements ConfigurableMockMvcBuilder<B> {

    @Override
    public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) {
        this.defaultRequestBuilder = requestBuilder;
        return self();
    }

    @SuppressWarnings("unchecked")
    protected <T extends B> T self() {
        return (T) this;
    }

}
1 Answers

the compiler asks for the explicit type:

Type inference failed: Not enough information to infer parameter T in 
fun <T : Nothing!> defaultRequest(requestBuilder: RequestBuilder) : T
Please specify it explicitly.

you can specify it like this:

@Component
class ApiKeyHeaderMockMvcBuilderCustomizer<T :Nothing?> : MockMvcBuilderCustomizer {
    override fun customize(builder: ConfigurableMockMvcBuilder<*>) {
        val apiKeyRequestBuilder: RequestBuilder = MockMvcRequestBuilders.get("any")
            .header("api-key-header", "apikeyvalue")
        builder.defaultRequest<T>(apiKeyRequestBuilder)
    }
}
Related