Ответ 1
Эти предупреждения возникают из-за веселого пересечения между разрешением перегрузки, типом ввода и типом вывода. Компилятор немного задумывается о вас и предупреждает вас, потому что большинство lambdas написаны без явно объявленных типов. Например, рассмотрите этот вызов:
test(1, i -> { });
Каков тип i
? Компилятор не может вывести его до тех пор, пока не завершит разрешение перегрузки... но значение 1
соответствует всем четырем перегрузкам. Независимо от того, какая перегрузка выбрана, это повлияет на целевой тип второго аргумента, который, в свою очередь, повлияет на тип, который был выведен для i
. Здесь действительно недостаточно информации для компилятора, чтобы решить, какой метод вызывать, поэтому эта строка фактически приведет к ошибке времени компиляции:
error: reference to test is ambiguous
both method test(float,Consumer<Float>) in Test and
method test(double,Consumer<Double>) in Test match
(Интересно, что он упоминает перегрузки float
и double
, но если вы прокомментируете один из них, вы получите ту же ошибку в отношении перегрузки long
.)
Можно представить себе политику, в которой компилятор завершил разрешение перегрузки с использованием наиболее специфического правила, тем самым выбрав перегрузку с аргументом int
. Тогда он будет иметь определенный целевой тип для применения к лямбда. Дизайнеры-компиляторы чувствовали, что это слишком сложно, и что будут случаи, когда программисты будут удивлены тем, что перегрузка закончилась. Вместо того, чтобы компилировать программы, возможно, непредвиденным образом, они чувствовали, что было бы безопаснее сделать это ошибкой и заставить программиста устранить эту проблему.
Компилятор выдает предупреждения в объявлениях метода, чтобы указать, что вероятный код, который программист написал бы для вызова одного из этих методов (как показано выше), приведет к ошибке времени компиляции.
Чтобы устранить неоднозначность вызова, вместо этого нужно было бы написать
test(1, (Integer i) -> { });
или объявить какой-либо другой явный тип для параметра i
. Другой способ - добавить бросок перед лямбдой:
test(1, (Consumer<Integer>)i -> { });
но это, возможно, хуже. Вероятно, вы не хотите, чтобы вызывающие абоненты вашего API должны были бороться с такими вещами в каждой точке вызова.
Эти предупреждения не возникают для случая Supplier
, так как тип Поставщика может быть определен с помощью локальных рассуждений без каких-либо выводов типа.
Вы, вероятно, захотите переосмыслить то, как вы объединяете этот API. Если вам действительно нужны методы с этими типами аргументов, вы можете переименовать методы testInt
, testLong
и т.д. И полностью перегрузить. Обратите внимание, что API-интерфейсы Java SE сделали это в подобных случаях, например comparingInt
, comparingLong
и comparingDouble
на Comparator
; а также mapToInt
, mapToLong
и mapToDouble
на Stream
.