Java не может найти правильный перегруженный метод с оператором двойной толстой кишки

При использовании оператора двойной колонки для обращения к перегруженному методу Java, похоже, не может определить правильный метод для использования. Рассмотрим этот пример:

public class A {
    private void setter(final Number value) { }
    private void setter(final Optional<Number> value) { }
    private void setter2(final Optional<Number> value) { }

    private <T> void useSetter(final Consumer<Optional<T>> a) { }

    private void callMethod() {
        useSetter(this::setter); // Error here
        useSetter(this::setter2);
    }
}

Первый вызов useSetter не компилируется и дает следующие ошибки:

Cannot infer type argument(s) for <T> useSetter(Consumer<Optional<T>>)
The type A does not define setter(Optional<Object>) that is applicable here

Однако второй вызов компилируется просто отлично, что означает, что проблема связана с перегрузкой setter. Только одна из перегрузок setter применима, поэтому я не понимаю, почему это не работает.

Об этом можно обойти, используя lambda, который задает тип параметра, но это намного более подробный.

useSetter((final Optional<Number> v) -> setter(v));

Есть ли лучший способ справиться с этой ситуацией или я столкнулся с этой странной причудой?

Ответ 1

Захват для компиляции вашего метода private <T> void useSetter(final Consumer<Optional<T>> a) { } - Optional<Object>. Компилятор пытается сказать вам, что он не может принуждать тип соответствовать любым известным захватам.

Main.java:12: error: incompatible types: cannot infer type-variable(s) T
        useSetter(this::setter); // Error here
                 ^
    (argument mismatch; invalid method reference
      no suitable method found for setter(Optional<Object>)
          method A.setter(Number) is not applicable
            (argument mismatch; Optional<Object> cannot be converted to Number)
          method A.setter(Optional<Number>) is not applicable
            (argument mismatch; Optional<Object> cannot be converted to Optional<Number>))
  where T is a type-variable:
    T extends Object declared in method <T>useSetter(Consumer<Optional<T>>)

Одним из решений является создание привязки с использованием private <T> void setter(final Optional<? super T> value) { } для универсального параметризованного необязательного типа. Другой вариант - подразумевать некоторую принудительную возможность для компилятора private void setter(final Optional<? super Number> value) { }.

class A<T> {
    private void setter(final Number value) { }
    private <T> void setter(final Optional<? super T> value) { }
    private void setter2(final Optional<Number> value) { }

    private <T> void useSetter(final Consumer<Optional<T>> a) { }

    private void callMethod() {
        useSetter(this::setter); // no more error
        useSetter(this::setter2);
    }

    public static void main(String [] args){

    }
}

class B {
    private void setter(final Number value) { }
    private void setter(final Optional<? super Number> value) { }
    private void setter2(final Optional<Number> value) { }

    private <T> void useSetter(final Consumer<Optional<T>> a) { }

    private void callMethod() {
        useSetter(this::setter); // no more error
        useSetter(this::setter2);
    }

    public static void main(String [] args){

    }
}

Вы можете проверить ideone здесь.

Ни одна из этих опций не идеальна, так как она добавит некоторую функцию для вашего кода, разрешив передать Optional<Object>, однако, если вы избегаете напрямую использовать исходные типы, вы должны быть в порядке.