Bogus Type Change Suggestion

  • Intellij version: 2020.1; SonarLint version: 4.6.0.16682
  • Minimal Sample Code:
import java.util.function.Function;

public class FunTypes {

    public static final Function<?, ?> NEW_OBJECT = anything -> new Object();
}

When faced with this code, SonarLint suggests:

Refactor this code to use the more specialised Functional Interface ‘UnaryOperator<?>’

? is not covariant with ?, so this suggestion doesn’t really make sense.

It sugests using UnaryFunction<?> because you only use one of the variables, not both. Function<?, ?> implies that you’ll be using 2 variables for your function.

Sure, I understand that in this specific little toy example, this would just happen to work. But that’s not the point. ? is not covariant with ?.

The type Function<?, ?> says “I will accept any object and I will return you something, and you don’t know what type it has”.

UnaryOperator<?> says “I will accept any object and return you something of the same type.”

Those do not mean the same thing. To better illustrate my point, consider the following code, which does not compile:

import java.util.function.Function;
import java.util.function.UnaryOperator;

public class FunTypes {

    public static final Function<?, ?> NEW_OBJECT = anything -> new Object();

    public static UnaryOperator<?> asUnaryOperator() {
        return t -> NEW_OBJECT.apply(t);
    }
}

The compiler rejects this because the ? that is accepted by apply() is not the same ? that is returned.

Here’s another example that might be more compelling. concreteAsUnary() compiles just fine, but wildcardAsUnary() does not compile due to “Incompatible equality constraint: capture of ? and capture of ?”

import java.util.function.Function;
import java.util.function.UnaryOperator;

public class FunTypes {

    public static final Function<?, ?> WILDCARDS = anything -> anything;
    public static final Function<Object, Object> CONCRETE = anything -> anything;

    public static UnaryOperator<?> wildcardAsUnary() {
        return asUnaryOperator(WILDCARDS);
    }

    public static UnaryOperator<Object> concreteAsUnary() {
        return asUnaryOperator(CONCRETE);
    }

    public static <T> UnaryOperator<T> asUnaryOperator(Function<T, T> f) {
        return f::apply;
    }
}

Okay, I understand. Sorry for the incorrect answer.

Hello,

I agree with you, this is a false positive, I created a ticket (SONARJAVA-3368) to track this issue.

Thanks for the feedback and examples, it is always appreciated.

Best,
Quentin

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.